合并“NSManagedObjectContextDidSaveNotification”后,NSFetchedResultsController 未显示所有结果

Posted

技术标签:

【中文标题】合并“NSManagedObjectContextDidSaveNotification”后,NSFetchedResultsController 未显示所有结果【英文标题】:NSFetchedResultsController is not showing all results after merging an `NSManagedObjectContextDidSaveNotification` 【发布时间】:2013-04-30 09:20:18 【问题描述】:

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

isTrash == NO

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

怎么了?

【问题讨论】:

NSFetchedResultsController with predicate ignores changes merged from different NSManagedObjectContext 的可能重复项 - 请注意,该问题的答案与您的解决方案略有不同。 @MartinR 我同意这些问题非常相似。但是是否可以合并它们以便我的答案仍然出现? (我认为我的答案优于其他问题的公认答案,因为我的答案更好地解释了问题并提供了更好的解决方案。) 我认为任何答案都不会因为一个问题被标记为重复而消失。但是我没有经验是否/何时/如何合并问题。 - 你的解释很好(类似于我的***.com/a/14021815/1187415 :-)。我不知道哪种解决方案更好,所以如果你有支持你的方法的论据,我很想知道它们! @MartinR 我认为我的解决方案优越的原因是使用existingObjectWithID:error:/refreshObject:mergeChanges: 而不是objectWithID:/willAccessValueForKey:existingObjectWithID: 将回避由于无效objectIds 引起的任何问题(尽管这不太可能发生),refreshObject:mergeChanges: 是一种更明确的强制获取对象的方式(willAccessValueForKey: 是对 KVO 的误用.willAccessValueForKey: 应该只在内部使用并且应该有一个匹配的didAccessValueForKey:)。 但您可以使用documented 来确保触发了故障。 【参考方案1】:

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

下面是它的结果:

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

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

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

    当有保存时,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];
            
                        
    );

【讨论】:

或者您只需将shouldRefreshRefetchedObjects 设置为YES 上的NSFetchRequest 获取结果控制器。 @lostintranslation UOV_async_main_thread 是一个宏。我已将其替换为对底层函数的调用。 @BenedictCohen 您的解释是深入评估 NSFRC 的起点。它为上述陈述带来了一些细微差别,并提出了替代解决方案。可以在这里找到:medium.com/bpxl-craft/…【参考方案2】:

尝试在您的获取结果控制器的NSFetchRequest 上将shouldRefreshRefetchedObjects 设置为YES

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

【讨论】:

此选项可能仅在您再次在 FRC 上调用 performFetch 时才有效。这里的问题是mergeChangesFromContextDidSaveNotification 不会更新当前上下文中没有加载的对象或者是错误的对象。因此,FRC 的自动更改跟踪不起作用。另请参阅***.com/a/14021815/1187415 以获取对该问题的类似分析。 (虽然我没有投反对票:-)

以上是关于合并“NSManagedObjectContextDidSaveNotification”后,NSFetchedResultsController 未显示所有结果的主要内容,如果未能解决你的问题,请参考以下文章

NSFetchedResultsController 错过了合并的 NSManagedObjectContext 的更新

带有谓词的 NSFetchedResultsController 忽略从不同 NSManagedObjectContext 合并的更改

核心数据:如何在两个 NSManagedObjectContext 之间合并插入/更新/删除,同时将合并保持为可撤消的步骤?

NSManagedObjectContext 通过通知中心问题在多个线程上保存/合并

NSManagedObjectContext 从私有上下文合并,只刷新改变的对象

数据源与后台 NSManagedObjectContext 合并后 UITableView 不会重新加载