NSFetchedResultsController does not show all results after merging `NSManagedObjectContextDidSaveNotification`

I have an NSFetchedResultsController that retrieves objects using a predicate:

isTrash == NO

In most cases, this works as expected, but when the object breaks, the resulting result controller does not retrieve the unencrypted object.

What will go wrong?

+4
source share
2 answers

The reason this happens is due to the way mergeChangesFromContextDidSaveNotification: handles updated objects. NSManagedObjectContext stores a record of objects that are used in context, they are called registered objects ( NSManagedObjectContext has methods for accessing and conditionally fetching registered objects). mergeChangesFromContextDidSaveNotification: only processes updates for objects that are registered in the context. This affects the NSFetchedResultsControllers , which explains the cause of the problem.

Here's how it goes:

  • FRC is configured with a predicate that does not match all objects (thus preventing the possibility that objects that do not match the predicate will be registered in the FRC context).

  • The second context does an object change, which means that it now matches the FRC predicate. The second context is maintained.

  • The FRC context processes NSManagedObjectContextDidSaveNotification , but only updates registered objects, so it does not update the object that now matches the FRC predicate.

  • FRC does not perform another selection when saving, therefore, does not know that the updated object must be included.

Correction

The solution is to retrieve all updated objects when merging the notification. Here is an example of a merge method:

 -(void)mergeChanges:(NSNotification *)notification { dispatch_async(dispatch_get_main_queue, ^{ NSManagedObjectContext *savedContext = [notification object]; NSManagedObjectContext *mainContext = self.managedObjectContext; BOOL isSelfSave = (savedContext == mainContext); BOOL isSamePersistentStore = (savedContext.persistentStoreCoordinator == mainContext.persistentStoreCoordinator); if (isSelfSave || !isSamePersistentStore) { return; } [mainContext mergeChangesFromContextDidSaveNotification:notification]; //BUG FIX: When the notification is merged it only updates objects which are already registered in the context. //If the predicate for a NSFetchedResultsController matches an updated object but the object is not registered //in the FRC context then the FRC will fail to include the updated object. The fix is to force all updated //objects to be refreshed in the context thus making them available to the FRC. //Note that we have to be very careful about which methods we call on the managed objects in the notifications userInfo. for (NSManagedObject *unsafeManagedObject in notification.userInfo[NSUpdatedObjectsKey]) { //Force the refresh of updated objects which may not have been registered in this context. NSManagedObject *manangedObject = [mainContext existingObjectWithID:unsafeManagedObject.objectID error:NULL]; if (manangedObject != nil) { [mainContext refreshObject:manangedObject mergeChanges:YES]; } } }); } 
+18
source

Try setting shouldRefreshRefetchedObjects to YES on NSFetchRequest for the selected result recipient.

This provides a more convenient way to ensure the properties of the managed object correspond to the storage values โ€‹โ€‹than when using refreshObject: mergeChanges: (NSManagedObjetContext) for several objects in turn.

-1
source

All Articles