UPDATE:
After spending a ton of time in Instruments it appears that I was saving out to Core Data a bunch of times by accident when I switch the views. To fix this, I'll have to rearrange a ton of how I load/save the data to/from the data entry fields in the subviews.
However, I did find that the major slow down caused when the number of Events increases is that for each Core Data save, something related to Core Data is doing a bunch of redrawing.
It appears that this is the original tableView doing an [UITableView _updateWithItems:updateSupport] and all the UIKit animation that goes with it. For the record, this is a stock UITableViewController with a segue to my editViewController using the prepareForSeque code below.
Is this what fetchedResultsControllers do? Keep the datasource for the tableView updated and the tableView is redrawing itself every time as the data changes, even though it's no longer visible?
(I've also pasted in the rest of the code that for the fetchedResultsController at the bottom of this post so you can see how I update the table...again, this is pretty boiler plate stuff except for a small modification I made for when new items are added to the table.)
ORIGINAL POST:
In my iOS app that uses Core Data, I have a UITableViewController that correctly queries Core Data and displays the "Event" objects that exist in the database (uses a fetchedResultsController).
When I segue from the tableView to an edit view, I pass the selected data object like so:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:@"EditEventSegue"]) {
// start a progress HUD
[SVProgressHUD showWithMaskType:SVProgressHUDMaskTypeBlack];
// pass the selected Event object to EditViewController during segue
NSIndexPath *indexPath = [[self eventListTable] indexPathForSelectedRow];
Event *theEvent = [_fetchedResultsController objectAtIndexPath:indexPath];
EditViewController *editController = segue.destinationViewController;
editController.theEvent = theEvent;
// set EventListViewController as the delegate for the EditViewController
editController.delegate = self;
}
}
The editController view has a property defined to hold the Event object as so:
@property (strong, nonatomic) Event *theEvent;
Everything works throughout the app by passing around this object between views (there are multiple views that get created/destroyed from the editController and they also have the same theEvent property defined).
When I want to save the data, I've built a protocol to send a "save" message up the view chain using delegation (maybe this isn't the best way) and the original tableViewController does the save (since it has the original managed object context).
The major problem in all this though, is that I'm finding that when I have lots of Event objects in the database, switching views slows right down! The more objects, the slower it gets! I'm not sure why since I'm just passing around a pointer to the one Event object selected in the original tableView.
Could it be because I've defined theEvent as "strong" in the detail view controller(s)? Should each view controller only have a weak reference?
Further, I've read about passing around the managed object context instead but even so, I'm not sure how to pass around a reference to the correct object other than how I'm doing it already. How would the various edit view controllers (or even the single editController you see in the code above) know which object is being edited?
Any ideas or suggestions would be helpful. Thanks!
EDIT:
This is code from the controller that contains the starting tableView:
- (NSFetchedResultsController *)fetchedResultsController {
if (_fetchedResultsController != nil) {
return _fetchedResultsController;
}
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:_managedObjectContext];
[fetchRequest setEntity:entity];
NSSortDescriptor *sort = [[NSSortDescriptor alloc] initWithKey:@"date" ascending:YES];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sort]];
[fetchRequest setFetchBatchSize:20];
NSFetchedResultsController *theFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:_managedObjectContext sectionNameKeyPath:nil cacheName:@"Root"];
self.fetchedResultsController = theFetchedResultsController;
_fetchedResultsController.delegate = self;
return _fetchedResultsController;
}
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller is about to start sending change notifications, so prepare the table view for updates.
[self.eventListTable beginUpdates];
}
- (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath {
UITableView *tableView = self.eventListTable;
switch(type) {
case NSFetchedResultsChangeInsert:
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeUpdate:
[self configureCell:(EventCell *)[_eventListTable cellForRowAtIndexPath:indexPath] atIndexPath:indexPath];
break;
case NSFetchedResultsChangeMove:
[tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id )sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type {
switch(type) {
case NSFetchedResultsChangeInsert:
[self.eventListTable insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
case NSFetchedResultsChangeDelete:
[self.eventListTable deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade];
break;
}
}
- (void)controllerDidChangeContent:(NSFetchedResultsController *)controller {
// The fetch controller has sent all current change notifications, so tell the table view to process all updates.
[self.eventListTable endUpdates];
// if a new event has just been added by the addEvent method then select it and segue to the Edit Details view
if (_justAddedNewEvent == YES) {
NSIndexPath *indexPath = [controller indexPathForObject:_newlyAddedEvent];
_justAddedNewEvent = NO;
// now open details view of newly added object
[self.eventListTable selectRowAtIndexPath:indexPath animated:YES scrollPosition:UITableViewScrollPositionBottom];
[self performSegueWithIdentifier:@"EditEventSegue" sender:self];
}
}