2013-10-10 4 views
1

Я не могу понять это, но у меня, похоже, есть null indexPath, когда я удаляю объект из NSFetchedResultsController.null indexPaths при удалении объекта

Когда я удалить свой объект, я делаю это:

- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath { 
    if (editingStyle == UITableViewCellEditingStyleDelete) { 
     // Delete the object 
     NSManagedObjectContext *context = [self.fetchedResultsController managedObjectContext]; 
     [context deleteObject:[self.fetchedResultsController objectAtIndexPath:indexPath]]; 

     [self saveContext]; 
    } 
} 

Настройка NSFetchedResultsController:

- (NSFetchedResultsController *)fetchedResultsController { 
    if (_fetchedResultsController != nil) { 
     return _fetchedResultsController; 
    } 
    NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init]; 
    [fetchRequest setFetchBatchSize:20]; 

    NSEntityDescription *entity = [NSEntityDescription entityForName:@"Route" inManagedObjectContext:self.managedObjectContext]; 
    [fetchRequest setEntity:entity]; 

    NSSortDescriptor *nameSort = [[NSSortDescriptor alloc] initWithKey:@"name" ascending:YES selector:@selector(localizedCaseInsensitiveCompare:)]; 
    NSArray *sortDescriptors = @[nameSort]; 
    [fetchRequest setSortDescriptors:sortDescriptors]; 

    NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Routes"]; 
    self.fetchedResultsController = aFetchedResultsController; 
    self.fetchedResultsController.delegate = self; 

    NSError *error = nil; 
    if (![self.fetchedResultsController performFetch:&error]) { 
     NSLog(@"Unresolved error with NSFetchedResultsController: %@", [error description]); 
     abort(); 
    } 

    return _fetchedResultsController; 
} 

Это где происходит сбой:

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { 

    switch (type) { 
      // Data was inserted - insert the data into the table view 
     case NSFetchedResultsChangeInsert: { 
      [self.savedRoutesTableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 

      break; 
     } 
      // Data was deleted - delete the data from the table view 
     case NSFetchedResultsChangeDelete: { 
      [self.savedRoutesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 
      break; 
     } 
     case NSFetchedResultsChangeUpdate: { 
      SavedRoutesTableViewCell *cell = (SavedRoutesTableViewCell *)[self.savedRoutesTableView cellForRowAtIndexPath:indexPath]; 
      [cell configureCellWithRoute:[controller objectAtIndexPath:newIndexPath]]; 
      break; 
     } 
     case NSFetchedResultsChangeMove: { 
      [self.savedRoutesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 
      [self.savedRoutesTableView insertRowsAtIndexPaths:@[newIndexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; 
      break; 
     } 
     default: 
      break; 
    } 
} 

В методе didChangeObject , оба индекса indexPath и newIndexPath равны нулю. Я могу NSLog мой объект, и я вижу объект. Он врезается в [self.savedRoutesTableView deleteRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic]; методе, за исключением:

CoreData: error: Serious application error. Exception was caught during Core Data change processing. This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification. *** -[__NSPlaceholderArray initWithObjects:count:]: attempt to insert nil object from objects[0] with userInfo (null) 

Когда я сохранить этот объект, я откладываю это так:

self.route.name = routeName; 
NSManagedObjectContext *tempContext = [self.route managedObjectContext]; 

[tempContext performBlock:^{ 
     NSError *error = nil; 
     if (![tempContext save:&error]) { 
      NSLog(@"an error occurred: %@", [error localizedDescription]); 
     } 

     [self.managedObjectContext performBlock:^{ 
      NSError *error = nil; 
      if (![_managedObjectContext save:&error]) { 
       NSLog(@"error in main context: %@", [error localizedDescription]); 
      } 
     }]; 
    }]; 

Я не совсем уверен, где еще отлаживать это, так как NSFetchedResultsController просто не возвращает мне indexPath для удаленного объекта. Есть предположения? Заранее спасибо.

Редактировать: Ну, я нашел виновника, вызвавшего ошибку, но я не уверен, почему это происходит. В основном у меня есть ViewController, который получает либо объект маршрута из основного MOC, если он редактирует маршрут, либо вставляет новый, если вы создаете новый маршрут. Поэтому в этом viewController, если я редактирую маршрут, потому что я пытаюсь использовать два MOC, один temp и один основной для своего родителя, поэтому я могу легко выбросить вещи, если пользователь решает отменить и не создать новый маршрут , Мне нужно было перевести этот маршрут в другой контекст, чтобы создать другой код, который у меня есть. Так что «передача» выглядит следующим образом:

NSManagedObjectContext *moc = _route.managedObjectContext; 
    NSManagedObjectID *routeId = [_route objectID]; 
    self.tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
    self.tempContext.parentContext = moc; 
    NSManagedObject *localRoute = [self.tempContext objectWithID:routeId]; 
    self.route = localRoute; 

С помощью этого кода, мое добавление на местах к существующему маршруту в настоящее время работает, что места находятся в тех же условиях, но как-то путает удаление существующего маршрута от основной MOC. Не знаете, почему и какое лучшее решение.

+0

Так как вы догадались, почему бы не опубликовать свое решения в качестве ответа, вместо помещая его в вопрос? Это будет легче для других, чтобы найти позже. – Makoto

ответ

0
NSManagedObjectContext *moc = _route.managedObjectContext; 
NSManagedObjectID *routeId = [_route objectID]; 
self.tempContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType]; 
self.tempContext.parentContext = moc; 
NSManagedObject *localRoute = [self.tempContext objectWithID:routeId]; 
self.route = localRoute; 

Должен был получить контекст, связанный с маршрутом.

+0

вы должны добавить некоторый контекст, чтобы объяснить и пометить этот ответ как принятый. – Kamchatka

0

Имел ту же проблему и решил ее, гарантируя, что NSFetchedResultsController всегда использует тот же NSManagedContext. Например, если вы выбрали объект из базы данных с помощью контроллера выборки, а затем вы хотите удалить этот объект, убедитесь, что в контроллере выборки используется тот же управляемый контекст, который он использовал при извлечении.

NSFetchedResultsController оптимизирован для работы с UITableView, а UITableView - это компонент пользовательского интерфейса, и пользовательский интерфейс всегда должен обрабатываться основным потоком, поэтому нет необходимости создавать новый NSManagedContext каждый раз, когда вы отправляетесь в fetch ...Поэтому реализация этого кода должна устранить эту проблему:

@synthesize fetchedResultsController = __fetchedResultsController; @synthesize managedObjectContext = __managedObjectContext;

- (NSManagedObjectContext *)managedObjectContext 
{ 
    if (__managedObjectContext != nil) 
    { 
     return __managedObjectContext; 
    } 

    NSPersistentStoreCoordinator *coordinator = ap.persistentStoreCoordinator; 
    if (coordinator != nil) 
    { 
     __managedObjectContext = [[NSManagedObjectContext alloc] init]; 
     [__managedObjectContext setPersistentStoreCoordinator:coordinator]; 
    } 
    return __managedObjectContext; 
} 

ар является указателем на AppDelegate:

ap = [[UIApplication sharedApplication] delegate]; 

Что делает этот код является создает один экземпляр MOC, а затем повторно его. Внимание: это не поточно, и это Безразлично должны быть (потому что вы должны использовать его только основной поток), так что если вы missuse это не будет работать ...

Использование что:

- (NSFetchedResultsController *)fetchedResultsController 
{ 
    if (__fetchedResultsController != nil) 
    { 
     return __fetchedResultsController; 
    } 
    //create __fetchedResultsController 
    // do some fetching 
    return __fetchedResultsController; 
} 

Итак, когда вы хотите, чтобы заполнить таблицу:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 
{ 
    return [[self.fetchedResultsController sections] count]; 
} 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 
{ 
    return [__fetchedResultsController.fetchedObjects count]; 
} 

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 
{ 
    static NSString *CellIdentifier = @"Cell"; 

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; 
    if (cell == nil) { 
     cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; 
    } 

    NSManagedObject *managedObject = [__fetchedResultsController objectAtIndexPath:indexPath]; 

    cell.textLabel.text = [managedObject valueForKey:@"smth."]; 

    return cell; 
} 

Если набор данных изменяется только:

__fetchedResultsController = nil; 
[tableView reloadData] 

И everyt Хины включая NSFetchedResultsControllers delegat методов не будет работать нормально, не нулевое indexPaths и так далее ...

Надеется, что я помог кому-то ...

Смежные вопросы