为什么核心数据无法将数据从专用上下文合并到主上下文



我有一个使用核心数据的应用程序,具有以下相当标准的托管对象上下文层次结构:

Persistent Store Coordinator 
↳ Save Context (Private Queue Concurrency Type) 
↳ Main Context (Main Queue Concurrency Type)
↳ Private Context (Private Queue Concurrency Type)

所有托管对象上下文的合并策略都设置为NSMergeByPropertyObjectTrumpMergePolicy

我正在观察NSManagedObjectContextDidSaveNotification,它将在保存私有上下文并将更改合并到主上下文时调用以下功能:

func contextDidSaveNotificationHandler(notification: NSNotification) {
if let savedContext = notification.object as? NSManagedObjectContext {
if savedContext == privateObjectContext {
mainObjectContext.performBlock({
if let updatedObjects = notification.userInfo![NSUpdatedObjectsKey] as? Set<NSManagedObject> {
//
// fire faults on the updated objects
//
for obj in updatedObjects {
mainObjectContext.objectWithID(obj.objectID).willAccessValueForKey(nil)
}
}
mainObjectContext.mergeChangesFromContextDidSaveNotification(notification)
})
}
}
}

这在大多数情况下都有效,但有时我发现对私有上下文中现有对象的更改没有合并到主上下文中。我不明白为什么——私有上下文保存是成功的;正在发送CCD_ 3通知;正在调用通知处理程序;CCD_ 4包含正确更新的对象;但最终,主上下文与私有上下文不同步。(即:主上下文中的托管对象与notification.userInfo?[NSUpdatedObjectsKey]中包含的值不同步)如果我终止应用程序并重新启动它,上下文将再次同步(从持久存储加载对象后)。

我在启动参数中启用了-com.apple.CoreData.ConcurrencyDebug 1,并且遵循了所有核心数据多线程规则。我看不出我的托管对象上下文层次结构或合并函数有任何明显的错误。可能是什么原因造成的?

我曾经使用与您类似的结构,但在我的情况下它并不可靠。有时它确实有效,有时却没有。其中一个错误是"不完全合并",正如您所描述的那样。我开始在iOS 10中观察这种行为。我相信核心数据的核心可能发生了一些变化。

不管怎样,我改变了主意。我开始在以下位置使用苹果的核心数据/并发示例代码:

https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/Concurrency.html#//apple_ref/doc/uid/TP40001075-CH24-SW3

如果你阅读了整个页面(没有那么大),你可能会注意到他们建议像往常一样创建一个私有队列,但随后:

使用NSPersistentContainer:

let jsonArray = …
let container = self.persistentContainer
container.performBackgroundTask() { (context) in
for jsonObject in jsonArray {
let mo = EmployeeMO(context: context)
mo.populateFromJSON(jsonObject)
}
do {
try context.save()
} catch {
fatalError("Failure to save context: (error)")
}
}

当然,我不确定上面的代码是否完全符合您的要求。然而,关键是要依靠persistentContainer来完成繁重的工作。

在所有数据被消耗并转换为NSManagedObject实例,在私有上下文上调用save在不阻塞的情况下将所有更改移动到主队列上下文中主队列。

不能合并同级上下文。您必须从父级向下进行合并。换句话说,改变

if savedContext == privateObjectContext {

if savedContext == savingObjectContext {

其中CCD_ 8是主上下文的父上下文。

您可以始终将mainQueueContext设置为privateQueueContext的父级:

privateObjectContext.parent = mainObjectContext

这将把保存的内容合并到主对象上下文中。我在一个项目中使用了这一点,在该项目中,我解析JSON,并在privateQueue的执行块中设置核心数据对象,将我的mainContext设置为父对象。然后我简单地保存私有,然后从主线程/主上下文访问数据。工作起来很有魅力。我应该补充一点,我没有把私有上下文保存在内存中,它是在我需要的时候创建的。

问题出在合并策略上。尝试将更改为NSErrorMergePolicy,我认为您将开始看到合并冲突。至少这会让你对的根本原因有更多的了解

最新更新