NSFetchedResultsController is not showing all results after merging an `NSManagedObjectContextDidSaveNotification`

回眸只為那壹抹淺笑 提交于 2019-11-30 10:15:37

The reason why this is happening is due to how mergeChangesFromContextDidSaveNotification: handles updated objects. NSManagedObjectContext keeps a record of objects which are in use in the context, these are referred to as registered objects (NSManagedObjectContext has methods for accessing and conditionally fetching registered objects). mergeChangesFromContextDidSaveNotification: only processes updates for objects which are registered in the context. This has a knock on effect for NSFetchedResultsControllers that explains the cause of the problem.

Here's how it plays out:

  1. A FRC is setup with a predicate that doesn't match all objects (thus preventing the objects which do not match the predicate from being registered in the FRCs context).

  2. A second context makes a change to an object which means that it now matches the FRCs predicate. The second context is saved.

  3. The FRCs context processes the NSManagedObjectContextDidSaveNotification but only updates its registered objects, therefore it does not update the object which now matches the FRC predicate.

  4. The FRC does not perform another fetch when there's a save, therefore isn't aware that the updated object should be included.

The fix

The solution is to fetch all updated objects when merging the notification. Here's an example 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's 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];
            }
        }                
    });
}

Try setting shouldRefreshRefetchedObjects to YES on the NSFetchRequest for your fetched results controller.

This provides more convenient way to ensure managed object property values are consistent with the store than by using refreshObject:mergeChanges: (NSManagedObjetContext) for multiple objects in turn.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!