First of all, sorry for the too long question.
I know there are a few questions here that discuss similar issues, but none of this talk about NSFetchedResultsController with a delegate along with the update in a separate thread. And none of the solutions helped me.
These are existing questions:
- NSFetchedResultsController: using NSManagedObjectContext during update crashes
- Determining which core attribute attribute / property change caused the NSFetchedResultsController update
- Core Data executeFetchRequest raises an NSGenericException (collection was mutated when enumerated)
- The collection was mutated upon listing. How to determine which set?
- and etc.
Now about my problem:
- I have a separate stream that updates the main data objects from the Internet (using a socket).
- There are several view controllers that display data from the same underlying data object (each tab contains a view controller that displays filtered data).
- Each view controller has its own instance of
NSFetchedResultsController , and the delegate is set to self.
Sometimes I get an exception was mutated while being enumerated when updating data in a separate thread, and sometimes it causes the application to crash.
I did a lot of code manipulation to try to fix this, and it seems like nothing helps.
I tried not to use the managed object directly from the datasource methods of the table. Instead, I created an array that contains a list of dictionaries. I populate these dictionaries with the didChangeObject method above. That way, I don't touch managed objects at all in the view controller.
And then I realized that the problem is in NSFetchedResultsController, which probably repeats the data all the time. And this is an object that conflicts with my updating data in a separate thread.
The question is, how can I update the main data objects in a separate thread when I have an NSFetchedResultsController with a delegate (which means that it "keeps track" of the data and constantly updates delagate).
NSFetchedResultsControllerDelegate:
- (void)controllerWillChangeContent:(NSFetchedResultsController *)controller { [self.tableView beginUpdates]; } - (void)controller:(NSFetchedResultsController *)controller didChangeObject:(id)anObject atIndexPath:(NSIndexPath *)indexPath forChangeType:(NSFetchedResultsChangeType)type newIndexPath:(NSIndexPath *)newIndexPath { if ( self.tabBarController.selectedIndex == 0 ) { UITableView *tableView = self.tableView; @try { switch(type) { case NSFetchedResultsChangeInsert: [tableView insertRowsAtIndexPaths:[NSArray arrayWithObject:newIndexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeUpdate: [tableView reloadRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationNone]; break; case NSFetchedResultsChangeMove: [tableView deleteRowsAtIndexPaths:[NSArray arrayWithObject:indexPath] withRowAnimation:UITableViewRowAnimationFade]; [tableView reloadSections:[NSIndexSet indexSetWithIndex:newIndexPath.section] withRowAnimation:UITableViewRowAnimationFade]; break; } } @catch (NSException * e) { NSLog(@"Exception in didChangeObject: %@", e); } } } - (void)controller:(NSFetchedResultsController *)controller didChangeSection:(id <NSFetchedResultsSectionInfo>)sectionInfo atIndex:(NSUInteger)sectionIndex forChangeType:(NSFetchedResultsChangeType)type { if ( self.tabBarController.selectedIndex == 0 ) { @try { switch(type) { case NSFetchedResultsChangeInsert: [self.tableView insertSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; case NSFetchedResultsChangeDelete: [self.tableView deleteSections:[NSIndexSet indexSetWithIndex:sectionIndex] withRowAnimation:UITableViewRowAnimationFade]; break; } } @catch (NSException * e) { NSLog(@"Exception in didChangeSection: %@", e); } } } - (void)controllerDidChangeContent:(NSFetchedResultsController *)controller { [self.tableView endUpdates]; }
In the datasource table data methods, I work directly with the managed object.
multithreading iphone core-data delegates nsfetchedresultscontroller
Michael Kessler
source share