4

我有一个 NSFetchedResultsController 使用谓词获取对象:

isTrash == NO

大多数情况下,这按预期工作,但是当一个对象被清除时,获取的结果控制器不会获取未清除的对象。

怎么了?

4

2 回答 2

20

发生这种情况的原因是由于如何mergeChangesFromContextDidSaveNotification:处理更新的对象。NSManagedObjectContext记录上下文中正在使用的对象,这些被称为注册对象(NSManagedObjectContext具有访问和有条件地获取注册对象的方法)。mergeChangesFromContextDidSaveNotification:仅处理在上下文中注册的对象的更新。这具有连锁反应,NSFetchedResultsControllers可以解释问题的原因。

以下是它的表现:

  1. 使用不匹配所有对象的谓词设置 FRC(从而防止不匹配谓词的对象在 FRC 上下文中注册)。

  2. 第二个上下文对对象进行了更改,这意味着它现在与 FRC 谓词匹配。第二个上下文被保存。

  3. FRC 上下文处理NSManagedObjectContextDidSaveNotification但仅更新其注册的对象,因此它不会更新现在与 FRC 谓词匹配的对象。

  4. FRC 不会在保存时执行另一次提取,因此不知道应该包含更新的对象。

修复

解决方案是在合并通知时获取所有更新的对象。这是一个示例合并方法:

-(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];
            }
        }                
    });
}
于 2013-04-30T09:20:18.267 回答
-1

尝试为您获取的结果控制器设置shouldRefreshRefetchedObjects为。YESNSFetchRequest

这提供了更方便的方法来确保托管对象属性值与存储一致,而不是依次对多个对象使用 refreshObject:mergeChanges: (NSManagedObjetContext)。

于 2013-04-30T09:29:54.963 回答