NSFetchedResultsController аварийно завершает работу на objectAtIndexPath?

Я получаю сбой при попытке получить доступ к объекту в NSFetchedResultsController,

2013-11-10 15:15:06.568 Social[11503:70b] CoreData: error: Serious application error.  Exception was caught during Core Data change processing.  This is usually a bug within an observer of NSManagedObjectContextObjectsDidChangeNotification.  *** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds for empty array with userInfo (null)
2013-11-10 15:15:06.570 Social[11503:70b] *** Terminating app due to uncaught exception 'NSRangeException', reason: '*** -[__NSArrayI objectAtIndex:]: index 2 beyond bounds for empty array'

viewDidLoad

- (void)viewDidLoad
{
    [super viewDidLoad];

    self.resultController = [DataEngine sharedInstance].postsFetchedResultController;
    self.resultController.delegate = self;
    [self.resultController performFetch:nil];

    [self.tableView reloadData];

    [[DataEngine sharedInstance] fetchInBackground];
}

Делегат контроллера результатов

#pragma mark - NSFetchedResultsControllerDelegate Methods -

- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView beginUpdates];
}

- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller
{
    [self.tableView endUpdates];
}

- (void)controller:(NSFetchedResultsController *)controller
   didChangeObject:(id)anObject
       atIndexPath:(NSIndexPath *)indexPath
     forChangeType:(NSFetchedResultsChangeType)type
      newIndexPath:(NSIndexPath *)newIndexPath
{
    switch (type)
    {
        case NSFetchedResultsChangeInsert:
            [self.tableView insertRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeUpdate:
            [self.tableView reloadRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeDelete:
            [self.tableView deleteRowsAtIndexPaths:@[indexPath]
                                  withRowAnimation:UITableViewRowAnimationAutomatic];
            break;

        case NSFetchedResultsChangeMove:
            [self.tableView moveRowAtIndexPath:indexPath toIndexPath:newIndexPath];
            break;

        default:
            break;
    }
}

вид таблицы

#pragma mark - UITableView Delegate & Datasrouce -

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return self.resultController.fetchedObjects.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    return [self cellForRowAtIndexPath:indexPath];
}

2 ответа

Решение

В одном из моих подклассов NSManagedObject у меня был следующий код, который вызывал проблему. NSSets автоматически инициализируются в NSManagedObejcts, и нет необходимости их инициализировать.

- (void)awakeFromFetch
{
    if (!self.comments)
        self.comments = [NSMutableSet set];

    if (!self.medias)
        self.medias = [NSMutableSet set];
}

Другая проблема заключалась в том, что во время вставки indexPath имеет значение null, вместо этого мне пришлось использовать newIndexPath

case NSFetchedResultsChangeInsert:
    [self.tableView insertRowsAtIndexPaths:@[newIndexPath]
                          withRowAnimation:UITableViewRowAnimationAutomatic];
    break;

Одной из причин сбоя в строке ниже может быть то, что вы обновляете self.resultContrlller после того, как использовали его для перенастройки количества строк в разделе таблицы. Убедитесь, что если вы постоянно обновляете свой объект self.resultContrlller, вы берете его копию и затем используете для рисования таблицы.

// This code does not prevent crash. Changing your regular code to modern objective-C way
Post *post = [self.resultContrlller fetchedObjects][indexPath.row];

Источник данных не должен изменяться во время перезагрузки таблицы. Вместо этого вы должны использовать копию fetchedObjects для загрузки таблицы. Итак, перед тем как перезагрузить таблицу, возьмите копию [[self.resultContrlller fetchedObjects] copy] и используйте ее для рисования таблицы. Ваш основной источник может продолжать меняться. И после перезагрузки таблицы с копией вы можете захотеть перезагрузить ее снова, если произошли изменения в данных. Такие сбои происходят, когда ваш источник данных изменяется быстрее, чем перезагружается таблица.

Другие вопросы по тегам