IPhone: NSFetchedResultsController with delegate and updating data from a separate stream

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.

+5
multithreading iphone core-data delegates nsfetchedresultscontroller
source share
1 answer

Two separate questions. Firstly, if you get mutating errors, it means that you mutate a set or array (or relation), iterate over that set / array / relation. Find where you are doing this and stop doing it. This is the only solution.

As for your updates. Your NSManagedObjectContext background should be kept periodically. Your main thread should listen for NSManagedObjectContextDidSaveNotification , and when it receives it, it calls the main NSManagedObjectContext in the main thread (since the notification most likely comes in the background thread) via -mergeChangesFromContextDidSaveNotification: which takes NSNotification as a parameter. This will cause all your NSFetchedResultController instances to run their delegation methods.

Just like that.

Update

ank you answer. An exception occurs when updating an NSManagedObjectContext in the background thread. I use the same NSManagedObjectContext in both threads. The application should be as close as possible to real-time applications - updates are ongoing, and tables should be updated immediately. I am not saving at all - I am only updating NSManagedObjectContext. I saw in one of the questions mentioned that someone used separate instances of NSManagedObjectContext, but he still gets the same exceptions as soon as he merges the changes. So, do you suggest using 2 separate NSManagedObjectContext files?

First read the multithreading in Core Data from the Apple documentation (or my book :).

Secondly, yes, you should have one context for the thread, which is one of the golden rules of Core Data and multithreading (the other does not pass instances of NSManagedObject over threads). This is probably the source of your failure, and if it does not, it will become a source of collapse in the future.

Update

I have a lot of data and I only update the changed / new / deleted items in the table. If I start saving, will it hurt performance?

No, only updates will be distributed in streams. The entire data warehouse will not be re-read, so it will really improve performance when you sort it into smaller pieces, because you will be in the main stream updating the user interface in small pieces to make the user interface look better.

However, worrying about performance before completing the application is a preliminary optimization that should be avoided. Guess what will work well and what generally will not be a bad idea.

+12
source share

All Articles