使用performBackgroundTask将多个核心数据插入调度到NSPersistentContainer时发生合并冲突

Max*_*lat 5 concurrency core-data ios swift

我正在尝试使用新的核心数据API NSPersistentContainer,并且给人的印象是内部排队机制将阻止写入事务同时进行评估,如以下堆栈溢出答案中 所述,用于保存到核心数据的NSPersistentContainer并发

许多专家长期以来一直在处理该问题(甚至在NSPersistentContainer做到这一点之前),其方式是让操作队列将写入排队,因此一次只能进行一次写入,并具有另一个上下文在主线程上仅用于读取。这样,您就永远不会遇到任何合并冲突。(有关此设置的详细说明,请参阅https://vimeo.com/89370886,这是NSPersistentContainer内部执行的操作)。当您调用performBackgroundTask时,persistentContainer使该块进入内部串行队列。这样可以确保不存在mergeConflicts。

但是,如果在performBackgroundTask每次循环使用的紧密循环中插入多个共享关系目标的实体,则在NSMergeConflict保存上下文时会出现错误:

            let bundlePath = Bundle.main.resourceURL!

        let directoryEnumerator = FileManager.default.enumerator(at: bundlePath, includingPropertiesForKeys: [URLResourceKey.isDirectoryKey, URLResourceKey.nameKey])
        while let url = directoryEnumerator?.nextObject() as? URL {

            if url.pathExtension == "jpeg" {
                let imageData = try! Data(contentsOf: url)

                DataManager.persistentContainer.performBackgroundTask { (context) in

                    //          context.mergePolicy = NSMergePolicy.overwrite

                    let new = Photo(context: context)
                    new.name = url.lastPathComponent
                    new.data = imageData as NSData

                    let corresponding = try! context.existingObject(with: DataManager.rootFolder.objectID) as! Folder
                    new.parent = corresponding

                    try! context.save()
                }
            }
Run Code Online (Sandbox Code Playgroud)

我在github上发布了一个示例项目来演示该问题:https : //github.com/MaximeBoulat/NSPersistentContainer_Merge_Conflict

崩溃似乎正在发生,因为多个实体正在为同一父级同时设置其“父级”关系,这会导致父级的“子级”关系在并发更新之间不同步。

即使我将.automaticallyMergesChangesFromParent传入上下文的属性设置为true,也会发生这种情况。我可以通过定义传入上下文的合并策略来防止崩溃,但这不是可接受的解决方案。

有什么方法可以配置NSPersistentContainer来正确序列化使用performBackgroundTaskAPI 调度的更新。还是我缺少某些东西导致这些更新彼此冲突?还是Apple为NSPersistentContainer堆栈提供了期望,即在评估传入的逻辑时遇到的任何冲突performBackgroundTask应该是致命的,还是可以忽略不计?

Jon*_*ose 3

我写了你引用的答案。我错了。我已经更新了。

我发现NSPersistentContainer'sperformBackgroundTask没有功能性的内部队列,它可能导致合并冲突。当我最初测试它时,似乎确实如此,但我像你一样发现可能存在冲突。幸运的是,通过创建自己的队列来解决这个问题并不难。我知道苹果发布如此糟糕的东西似乎很奇怪,但事实似乎就是如此。

我很抱歉发布了不正确的信息。

  • 它没有损坏,这是设计使然的。如果您想要这种行为,只需坚持并使用单个背景上下文即可。 (2认同)