Core Data 中的异步对象构造

Posted

技术标签:

【中文标题】Core Data 中的异步对象构造【英文标题】:Asynchronous Object Construction in Core Data 【发布时间】:2014-06-10 14:05:10 【问题描述】:

我目前正在开发一个具有相当复杂的核心数据模型的应用程序。数据模型目前有 10 个表,它们之间设置了一堆关系。模型的数据是从远程服务器中零碎获取的。为了尽量减少进出服务器的流量,服务器 API 首先传递对象 ID,让我有机会发现我是否已经存储了对象。如果没有,那么我可以向服务器询问完整的对象并存储它们。但是,这些对象可以引用其他对象,为此我需要检查是否遵循相同的过程:检查我是否有对象,如果没有,则从服务器获取对象。 Core Data 模型包括用于验证和构建 Core Data 对象图的服务器 ID 字段。

这会造成对象已经在 Core Data 中实例化,但还没有完全构造的情况,因为它们可能正在等待服务器返回引用的对象(反过来,服务器可能需要等待他们自己的参考对象)。

所以我第一次尝试处理这个问题是创建一个不允许保存对象上下文的信号量(我只将上下文保存在一个地方),直到所有对象都被下载并构建对象图。我遇到的问题是上下文被保存了,没有我问。这会导致大量更改通过NSFetchedResultsController 传播,因为从服务器下载对象并且正在构建对象图。此外,传播的对象可能不完整。

有人处理过这样的事情吗?我认为如果我可以明确控制 Core Data 何时保存,这一切都可以工作,但这似乎是不可能的。还是我错过了什么?

更新

错过了一些东西。我的印象是NSFetchedResultsController 在保存上下文时收到了更新。这不是真的。每当在上下文中调用processPendingChanges 时,它都会接收更新,这发生在事件循环的末尾。过去,我总是使用两个上下文来将更新与 UI 分开,但是这个项目有一个截止日期,并且现有代码使我无法进行重构。鉴于这些新信息,我认为单独的上下文将解决我的问题。

【问题讨论】:

你在使用父子上下文吗?如果是这样,如果您将更改传播到一个共同的父级,它们可能会在以后的保存中保留。您应该创建一个专用的子上下文,它将聚合更改,并仅在您准备好对象时保存它。它不会被系统保存。 这就是问题所在。它正在被我以外的其他人保存在某个地方。我对所有代码进行了搜索,以确保我没有在上下文中调用save:。我只在一个地方调用它。它没有被调用,但上下文被保存了。 【参考方案1】:

这是与服务器同步的一种极其昂贵的方式。您的服务器是否有理由无法响应“自 X 以来已更改”调用并为您提供一切?在您当前的设计中,您打开和关闭套接字的时间比接收数据的时间要多。

尽管如此,您希望在直接连接到NSPersistentStoreCoordinator 的辅助上下文中进行所有这些处理。当它保存时,您希望捕获NSManagedObjectContextDidSaveNotification,然后让您的 UI 上下文使用该通知。当您的服务器同步完成时,这将更新您的 UI。

这将使您的同步 100% 与 UI 隔离,并允许 UI 在您使用服务器时保存或执行它需要执行的任何其他操作。我不会在这里使用父/子设计。没有理由。

【讨论】:

它并不像他们之前那样昂贵,因为他们一直在传递所有的对象。抓住“新对象”是我正在推动的改变之一。无论如何,创建一个单独的上下文实际上是任务列表上的下一个,我只是误解了更改是如何传播到NSFetchedResultsController 并想在继续创建单独的上下文之前解决这个问题(或澄清我的理解)。看起来前进实际上是解决办法。 另外,我的目标不是将所有服务器数据与客户端同步(这将是大量的),而是仅获取相关信息。这就是为什么我使用对象引用并且只获取那些对象。【参考方案2】:

您通过 NSManagedObjectContext 类访问核心数据数据库。

每个上下文对象必须属于一个线程,并且上下文创建的任何 NSManagedObjects 都属于同一个线程。

不要从创建它的线程以外的线程读取或写入任何托管对象。如果这样做,您最终会遇到不可预测且无法调试的数据损坏问题。

但是,您可以为单个核心数据数据库拥有多个 NSManagedObjectContext 实例,每个实例位于不同的线程上,并且您可以将一个线程中对上下文所做的任何更改合并到另一个线程上的上下文中。

所以,基本上,您有一个“主”NSManagedObjectContext,它在主线程上使用,并用于几乎所有操作。然后,当您需要在另一个线程上做某事时,您为该线程创建一个“子”上下文,进行所有更改,然后将这些更改合并回主线程上的主上下文。

您可以从 Apple 的官方文档中找到如何实现这一点的具体细节。从这里开始阅读:

https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CoreData/Articles/cdConcurrency.html#//apple_ref/doc/uid/TP40003385-SW1

【讨论】:

我知道如何使用多个上下文。事实上,这是我在项目中的下一步。它是原始设计的一部分,但由于截止日期而被推迟(进入下一个项目阶段)。不过,这可能是问题所在。我过去一直使用多个上下文...这是我第一次使用单个上下文来方便。 @AaronHayman 所以你有一个上下文并且你在两个线程上与之交谈?你不能那样做。严重地。如果您设置保护措施以确保在不同的线程上没有同时与它进行对话,文档清楚地说明永远不要在任何上下文或托管对象上调用任何方法,或者从创建的线程以外的线程获取结果控制器上下文。 没有。我有一个上下文,一个线程。设置另一个私有上下文来执行同步操作是我的下一个任务(我现在正在做的事情)。

以上是关于Core Data 中的异步对象构造的主要内容,如果未能解决你的问题,请参考以下文章

使用 xcode 和 core-data 中的代码创建托管对象

如何根据 Core Data 中的关系对象创建 UITableView 部分

既然 objectID 在临时对象和永久对象之间发生变化,如何有效地处理 Core Data 中的临时对象?

在 Restkit 执行映射操作之前删除 Core data 中的现有对象

Core Data / NSFetchedResultsController - 注册与获取的对象相关的更改对象

Core Data 中的可变和不可变托管对象模型有啥区别?