【发布时间】:2020-07-20 09:01:49
【问题描述】:
我们有一个 iOS 应用程序,它使用 Core Data 来保存从私有 Web API 获取的记录。我们的一个 API 请求获取Project 记录的列表,每个记录都有多个关联的Location 记录。 ObjectMapper 用于反序列化 JSON 响应,我们有一个自定义转换器,它将嵌套的 Location 属性分配给 Project 实体上的核心数据关联。
代码的相关部分如下所示。它在 PromiseKit 承诺中执行(因此是 seal),我们首先保存到后台上下文,然后传播到 UI 线程上使用的主上下文。
WNManagedObjectController.backgroundContext.perform {
let project = Mapper<Project>().map(JSONObject: JSON(json).object)!
try! WNManagedObjectController.backgroundContext.save()
WNManagedObjectController.managedContext.performAndWait {
do {
try WNManagedObjectController.managedContext.save()
seal.fulfill(project.objectID)
} catch {
seal.reject(error)
}
}
}
我们遇到的问题是这个插入过程将每个Location 记录保存到数据库两次。奇怪的是,重复的 Location 记录与其父 Project 记录没有任何关联。也就是说,如果用NSFetchRequest查找Location记录,或者如果我在底层SQLite数据库上运行查询,我可以看到每个Location有两个条目,但只有project.locations返回每个Location 的一份副本。应用于具有相同结构的其他记录类型的相同(或非常相似)过程也会导致重复。
到目前为止,我已经尝试了几种方法来缩小问题范围:
- 检查了 API JSON - 没有重复。
- 在写入核心数据之前检查了
project.locations属性的状态。在对象被持久化之前不存在重复记录,这表明反序列化器和自定义嵌套属性转换器工作正常。 - 删除了将更改传播到主线程托管对象上下文的块,以防这导致插入发生两次。仅通过写入后台上下文仍然会得到重复项。
- 设置
com.apple.CoreData.ConcurrencyDebug 1运行应用程序。在这个过程中没有抛出异常,确认它不是某种线程安全问题。 - 设置
com.apple.CoreData.SQLDebug 1运行应用程序。我可以在日志中看到 Core Data 在底层 SQLite 数据库中插入的Location行数正好是预期的两倍。 - 对实体实施了唯一性约束。这解决了持久化数据方面的问题,但除非设置了
NSMergePolicy,否则仍会引发错误。
该列表中的最后一项有效地解决了问题,但它治疗的是症状,而不是原因。数据完整性对我们的应用程序很重要,我希望了解潜在的问题可能是什么,或者我可能会寻求其他选项来进一步调查。
谢谢!
【问题讨论】:
-
关系是否有逆向关系?
-
@pbasdf 是的,这种关系确实有相反的关系。
-
我会尝试覆盖 Location 类的 awakeFromInsert,放置一个断点,然后查看回溯是否为您提供任何关于调用插入的代码的线索。
-
@pbasdf 感谢您的建议!我试过这个,
awakeFromInsert确实为每个Location调用了两次——一次用于后台上下文,来自Location构造函数,然后当更改传播到父上下文时再次调用。 This question 表示这很正常,所以我不确定重复调用是否有任何意义。 -
因此,对于每个位置,两次调用 awakefrominsert - 但对于 JSON 中的每个位置,仍然有两个 Location 对象(因此 4 次 awakefrominsert 调用)。回溯是否提供有关导致插入的代码的任何信息?
标签: ios swift core-data objectmapper