NSFetchedResultsController crashes on objectAtInde

2020-05-03 11:47发布

问题:

I'm getting a crash when trying to access an object in 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];
}

Result Controller Delegate

#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;
    }
}

table view

#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];
}

回答1:

One reason for crash on below line could be that you are updating self.resultContrlller after you have used it for retuning the number of rows of your table section. Make sure that if you are constantly updating your self.resultContrlller object then you take a copy of it and then use for your table drawing.

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

The data source should not change while table is reloading itself. You should use a copy of fetchedObjects instead to load the table. So, evey time, before you reload your table, take a copy [[self.resultContrlller fetchedObjects] copy] and the use it for table drawing. Your main source can then keep on changing. And after table reload is done with copy you may want to reload it it again if there was a change in the data. Such crashes happens when your data source changes faster than table reloads.



回答2:

In one of my NSManagedObject subclasses I had the follwiing code which was causing the issue. NSSets are automatically initialized on NSManagedObejcts and there is no need to initialize them.

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

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

Another problem was that during insert indexPath is null, I had to use newIndexPath instead

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