在向 Core Data 保存数据时,很多时候,我们都不希望保存进重复的记录。但 NSManagedObjectContext
中却很有可能包含相同的内容。一个典型的例子就是,先从 Core Data 缓存中读取了数据,然后,再通过服务器拉取内容,如果这两部分内容创建在同一个 NSManagedObjectContext
对象里,其中就非常有可能包含重复的内容。之后,如果再把这个上下文对象写回 Core Data,就会发生记录重复的问题了。例如,我们在 managedContext
创建两个相同的 Series
对象:
let series = Series(context: managedContext)
series.id = 1
series.title = "Build Boxue App"
series.summary = "Let's build boxue App from scratch."
let series1 = Series(context: managedContext)
series1.id = 1
series1.title = "Build Boxue App"
series1.summary = "Let's build boxue App from scratch."
默认条件下,managedContext
写入 Core Data 的时候,就会生成下面这样的记录::

多数情况下,这都不是我们想要的结果。为了解决这个问题,我们要从两个地方下手。
在 ENTITIES 中添加约束
首先,要在定义 ENTITY 的时候,指定希望哪些字段在 Core Data 中是唯一的。例如 Series,我们可以像这样指定 id:

设置 constraints 的时候,要选中 ENTITY,不要选中它的某个属性,就可以在右边的 inspector 里,设置约束了。
类似地,用同样的方法,我们给 History 和 Episode,也设置上 id 的唯一约束。
设置 NSManagedObjectContext 对象
其次,就是像上一节提到的,设置 NSManagedObjectContext
对象的内容合并策略:
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
它表示当 viewContext
中的内容和 Core Data 存储中的内容有冲突时,使用 viewContext
的值覆盖 Core Data 中的值。并且,由于我们给 ENTITY 设置了约束,这个覆盖的过程,会遵守这个约束,保持特定字段的唯一性。这样一来,即便一开始 managedContext
包含了多个重复的 Series
对象,写回到 Core Data 的时候,也就只有一条记录了。
看到这,你可能会想,如果设置了约束,但是没设置合并策略会如何呢?为了看到这个结果,可以把 mergePolicy
的代码注释掉重新执行,就会在控制台看到类似下面这样的错误了:

红框的部分告诉我们,某个字段在写入 Core Data 的时候,发生了冲突。
What's next?
以上,就是如何在 Core Data 中确保数据唯一性的方法。了解了它之后,现在我们思考这样一个场景:当在项目中使用 CoreDataManager
时,它应该是创建在主线程的。但通常,从服务器获取数据都发生在后台线程。遗憾的是,我们并不能在后台线程中直接向主线程中的 managedContext
写入数据,这样做会导致运行时错误。
但别担心,这并不意味着 Core Data 不能在多线程环境中工作,只是需要一些固定的套路而已。下一节,我们通过一个具体的例子,来了解 Core Data 中的多线程访问规则。