为什么Core Data无法将数据从私有上下文合并到主上下文?

时间:2017-01-28 02:06:13

标签: ios swift core-data

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

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)
            })
        }
    }
}

这大部分时间都在工作,但有时我发现私有上下文中对现有对象的更改未合并到主上下文中。我无法弄清楚为什么 - 私有上下文保存是成功的;正在发送NSManagedObjectContextDidSaveNotification通知;正在调用通知处理程序; notification.userInfo?[NSUpdatedObjectsKey]包含正确更新的对象;但最后,主要上下文与私有上下文不同步。 (即:主上下文中的托管对象与notification.userInfo?[NSUpdatedObjectsKey]中包含的值不同步)如果我终止应用程序并重新启动它,则上下文会再次同步(从持久性存储中加载对象后)。

我在启动参数中启用了-com.apple.CoreData.ConcurrencyDebug 1,并且正在遵循所有Core Data多线程规则。我无法看到管理对象上下文层次结构或合并功能的任何明显错误。可能导致这种情况的原因是什么?

4 个答案:

答案 0 :(得分:0)

您无法合并兄弟情境。您必须从父级合并。换句话说,改变

SplitText

 if savedContext == privateObjectContext {

其中 if savedContext == savingObjectContext { 是主要上下文的父上下文。

答案 1 :(得分:0)

您可以随时将mainQueueContext设置为privateQueueContext的父级:

privateObjectContext.parent = mainObjectContext

这会将您的保存合并到主对象上下文中。我在一个解析JSON的项目中使用它,并在privateQueue的perform块中设置核心数据对象,并将mainContext设置为父级。然后我简单地保存私有,然后从主线程/主上下文访问数据。奇迹般有效。我应该补充一点,我不会将私有上下文保留在内存中,而是在需要时创建它。

答案 2 :(得分:0)

我过去常常使用与你类似的结构,但在我的情况下它并不可靠。有时它确实有效,有时则没有。其中一个错误是“不完全合并”,就像你描述的那样。我开始在iOS 10中观察到这种行为。我相信Core Data的核心可能会发生一些变化。

无论如何,我改变了方法。我开始在Core Data / Concurrency上使用Apple的示例代码:

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,其中   将所有更改移动到主队列上下文中而不会阻塞   主要队列。

答案 3 :(得分:-1)

问题在于您的合并政策。尝试更改为NSErrorMergePolicy,我认为您将开始看到合并冲突。至少这将使您更多地了解根本原因