I'm using an NSFetchedResultsController in a standard way to update a UITableView whenever something changes in the related core-data entities. I'm doing the same as described in the Apple documentation.
The problem I have is when I make a mass insert of new core-data entities. This causes the NSFetchedResultsController delegate to allocate (and insert) a new cell for each entity, but it does this without recycling the UITableViewCells (i.e., dequeueReusableCellWithIdentifier:
always returns null). This means the allocation of potentially 100s of UITableViewCells, which can lead to memory problems. Does anyone know of a fix or workaround? Thanks.
Edit 1:
Within my UITableViewController subclass I have the standard NSFetchedResultsControllerDelegate methods. I believe this is identical to the example from Apple.
-(void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.tableView;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationTop];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:[tableView cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationBottom];
// Reloading the section inserts a new row and ensures that titles are updated appropriately.
[tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationTop];
break;
}
}
-(void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationTop];
break;
case NSFetchedResultsChangeDelete:
[self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationBottom];
break;
}
}
-(void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.tableView endUpdates];
}
Also within my UITableViewController subclass I have the following for fetching a cell for a given indexPath:
-(void)configureCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {
Waypoint *waypoint = [self.fetchedResultsController objectAtIndexPath:indexPath];
cell.textLabel.text = [waypoint comment];
}
-(UITableViewCell *)tableView:(UITableView *)aTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
static NSString *CellIdentifier = @"WaypointCell"; // matches identifier in XIB
UITableViewCell *cell = [aTableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
NSLog(@"new cell");
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
} else {
NSLog(@"recycled cell");
}
[self configureCell:cell atIndexPath:indexPath];
return cell;
}
In the tableView:cellForRowAtIndexPath:
function I added NSLog statements to display what's going on.
Here is what's happening. The above mentioned UITableViewController is pushed onto the navigation stack. An asynchronous request goes out and fetches a bunch of data, and creates or modifies the data related to this fetchController. Once [self.tableView endUpdates]
gets called, the system begins creating and inserting the UITableViewCells into the UITableView. In the debugger console the output "new cell" is printed multiple times (can number in the 100s), which I believe is one for each new entity created. Only after the tableview is loaded (if it didn't crash due to memory problems) and I begin scrolling do I see the "recycled cell" output in the console.