Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
325 views
in Technique[技术] by (71.8m points)

core data - NSRangeException exception in NSFetchedResultsChangeUpdate event of NSFetchedResultsController

I have a UITableView that uses an NSFetchedResultsController as data source.

The core data store is updated in multiple background threads running in parallel (each thread using it's own NSManagedObjectContext).

The main thread observes the NSManagedObjectContextDidSaveNotification notification and updates it's context with mergeChangesFromContextDidSaveNotification:.

Sometimes it happens that the NSFetchedResultsController sends an NSFetchedResultsChangeUpdate event with an indexPath that does not exist anymore at that point.

For example: The result set of the fetched results controller contains 1 section with 4 objects. The first object is deleted in one thread. The last object is updated in a different thread. Then sometimes the following happens:

  • controllerWillChangeContent: is called.
  • controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: is called with type = NSFetchedResultsChangeDelete, indexPath.row = 0.
  • controller:didChangeObject:atIndexPath:forChangeType:newIndexPath: is called with type = NSFetchedResultsChangeUpdate, indexPath.row = 3.

But the fetched results controller contains only 3 objects now, and if call

MyManagedObject *obj = [controller objectAtIndexPath:indexPath]

to update the table view cell according to the NSFetchedResultsChangeUpdate event, this crashes with a NSRangeException exception.

Thank you for any help or ideas!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

I have now found a solution for my problem. In the case of an update event, there is no need to call

[self.controller objectAtIndexPath:indexPath]

because the updated object is already supplied as the anObject parameter to the -controller:didChangedObject:... delegate.

I have therefore replaced -configureCell:atIndexPath: by a -configureCell:withObject: method that uses the updated object directly. This seems to work without problems.

The code looks now like this:

- (void)configureCell:(UITableViewCell *)cell withObject:(MyManagedObject *)myObj
{
    cell.textLabel.text = myObj.name;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MyCellIdentifier"];
    [self configureCell:cell withObject:[self.controller objectAtIndexPath:indexPath]];
    return cell;
}

- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath
     forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath
{
    UITableView *tableView = self.tableView;

    switch(type) {
    /* ... */           
    case NSFetchedResultsChangeUpdate:
        [self configureCell:[tableView cellForRowAtIndexPath:indexPath] withObject:anObject];
        break;
    /* ... */           
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...