合并“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:
将回避由于无效objectId
s 引起的任何问题(尽管这不太可能发生),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 通过通知中心问题在多个线程上保存/合并