The problem is inside your configureCell:atIndexPath
method. Unfortunately the implementation that is provided in the Core Data Xcode templates has a few bugs. Your crash is one of them.
The NSFetchedResultsController
method objectAtIndexPath:
is not very safe to call. If you give it an NSIndexPath that is out of range it will crash with the very exception you are seeing. This is mentioned in the class reference:
If indexPath does not describe a valid index path in the fetch results, an exception is raised.
This is just like access to an array: whenever you access an array by an index, you should do a bounds check first. A bounds check for objectAtIndexPath:
would look like:
id result = nil;
if ([[self.fetchedResultsController sections] count] > [indexPath section]){
id <NSFetchedResultsSectionInfo> sectionInfo = [[self.fetchedResultsController sections] objectAtIndex:[indexPath section]];
if ([sectionInfo numberOfObjects] > [indexPath row]){
result = [self.fetchedResultsController objectAtIndexPath:indexPath];
}
}
And that's a bit complicated, but necessary. That solves the exception.
The reason you are getting an exception is that the state of the NSFetchedResultsController is getting out of sync with the state of the tableview. When your crash happens the tableView just asked your delegate methods (numberOfSectionsInTableView
and tableView:numberOfRowsInSection:
for the number of rows and sections. When it asked that, the NSFetchedResultsController had data in it, and gave positive values (1 section, 3 rows). But in-between that happening and your crash, the entities represented by the NSFetchedResultsController were deleted from the NSManagedObjectContext. Now cellForRowAtIndexPath:
is being called with an outdated indexPath
parameter.
It looks like egoRefreshScrollViewDataSourceDidFinishedLoading
is causing the tableView to be rebuilt by adjusting the content view of the scroll view - and cellForRowAtIndexPath:
to be called - before you refetch and reload your tableView. That needs to happen before whatever egoRefreshScrollViewDataSourceDidFinishedLoading
is doing. egoRefreshScrollViewDataSourceDidFinishedLoading
is causing cellForRowAtIndexPath:
to be called with stale index paths.
The real problem may lie in your NSFetchedResultsControllerDelegate
methods. When your entities are being deleted, those should be getting called to remove items from the tableView. That does not seem to be happening here.
The short version though, move your [self.tableView reloadData];
up one line:
[self.tableView reloadData];
[_refreshHeaderView egoRefreshScrollViewDataSourceDidFinishedLoading:self.tableView];
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…