1

我在 Apple 的文档中找不到简单、常见的模式:

  1. 加载核心数据存储
  2. 下载新数据,在内存中创建对象
  3. 将一些新数据保存到存储中(通常“只有新的位/未更改的位”)

相反,我可以找到这些替代方案,但没有一个是正确的:

  1. 不要在内存中创建对象(嗯,这意味着丢弃所有关于对象的好处。使用大量 NSDictionary 编写代码,除了解决 CoreData 的故障之外没有其他用途。一般不可行)
  2. 创建对象,然后删除您不想要的对象(Apple 在文档中建议这样做,但他们的通知出现严重错误:当您尝试保存时,那些“删除”会出现,即使它们不应该/不能)
  3. 在辅助上下文中创建对象(Apple 强烈暗示这是正确的,但显然没有提供任何方法让您将对象从临时上下文移动到真实上下文,而不执行上述操作(删除您刚刚创建的对象,然后执行保存)。这通常是不可能的,因为对象通常需要连接到新上下文中的引用,并且保存会失败)

果然,应该没这么难吧?

如果我必须编写所有代码来手动深度复制一个对象(通过向下迭代它的所有字段和数据结构),为什么 CoreData 首先存在?这是 CD 内部提供的基本功能。

到目前为止,我工作的唯一解决方案是选项 2(来自苹果的文档),当 Apple 为原本不应该保存的对象发送 NSNotifications 时,使用自定义启发式“猜测”(但 Apple 无论如何都会发送通知) )。这是一个可怕的黑客。

编辑:澄清:

我不知道如何正确发送 Apple 的通知。Apple 的代码似乎将插入转换为“更新”,将“临时对象”转换为“删除”等。我听不到“新对象”。

4

3 回答 3

2

有 3 种常见的方法来处理这个问题。

  1. 使用与“真实”上下文相同的持久存储创建一个临时对象上下文,将对象添加到此临时上下文中,一旦知道要保留哪些对象,就从临时上下文中删除所有其他对象并保存临时上下文。保存时,您可以通过观察 NSManagedObjectContextDidSaveNotification 通知来更新您的“真实”上下文并将其合并到您的“真实”上下文中(ala [realContext mergeChangesFromContextDidSaveNotification:notification])。有关详细信息,请参阅 Mike Weller 的答案

    (如果你关心 I/O,你可以使用内存上下文,它有利有弊。)

  2. 不要使用 NSManagedObject,而是使用 NSDictionary。一旦您知道要保留哪些对象,就实例化一个新的托管对象并调用 [managedObject setValuesForKeysWithDictionary:temporaryObject] 将临时对象中的值复制到托管对象中,然后保存您的“真实”上下文。如果您的代码需要使用 NSManagedObjects 和临时对象(例如,表格视图),您将使用键值编码(又名 valueForKey:、setValue:forKeyPath:)来编写该代码。

  3. 将“isTemporary”属性添加到您的实体模型(默认为 NO)。创建临时对象时,将 isTemporary 设置为 YES 并将对象插入到“真实”上下文中。一旦知道要保留哪些对象,请将它们的 isTemporary 属性更改为 NO。当然,您需要定期删除这些临时对象,但这很容易做到(例如,当该任务完成时,在应用程序退出时等)。

#1 和 #3 的优点是你的对象存在于 CoreData 世界中——例如,它们可以被查询,它们可以参与关系等等。#2 的优点是它轻巧快速,特别是如果你有很多临时对象。

于 2011-04-03T04:08:23.550 回答
1

似乎选项 3 是最好的选择。

编辑:在 iOS 4 上广泛使用后,我会说“总是使用 NSOperationQueue 而不是 performSelectorOnBackgroundThread”。如果您不知道如何以简单的方式使用 NSopQ,请 google 一下,但它可以在不到 3 行代码中完成,因此与使用 performSel 相比只有很小的变化。它与 iOS4 的新线程调度程序配合得更好。

基于“我怎么能强迫它工作?”,我想出了这种方法:

  1. 原始类必须有自己的上下文。它必须订阅以在其私有上下文上收听“更改”(通过 NSNotification)。
  2. 仅使用“performSelectorOnBackgroundThread”或类似方法调用下载方法(强制它们使用不同的线程)
  3. 始终将不是 NSManagedObjects 的参数传递给上述方法调用,并且不要引用它们(这是通过使用 performSelector 强制执行的......无论如何 - 但即使你在同一个线程上,它也会在稍后搞砸 Apple 的代码,如果你做任何其他方式)
  4. 始终为新对象需要连接的“预先存在的”托管对象提供 ID
  5. 始终在开始下载之前创建一个新的临时 NSManagedContext ,并且:...
  6. ...始终将您正在运行的原始类注册(使用 NSNotifications)到此“临时”上下文的“保存”
  7. 下载、创建对象、删除不需要的对象
  8. 然后总是重新获取(在临时上下文中)您通过 ID 传入的对象,并将它们连接到新创建的对象
  9. 保存临时上下文
  10. ORIGINAL 类通过重新调用回调但在主线程上对“保存的上下文”做出反应(如果还没有在主线程上 - [NSThread isMainThread])
  11. ORIGINAL 类一旦在主线程上执行,就会使用 Apple 的“合并”方法将 NSNotificaiton 对象合并到自己的存储中
  12. ORIGINAL 类通过处理更改来对“更改的上下文对象”做出反应

但是......这还需要Apple的文档没有提到的东西:永远不要保存对任何托管对象的引用,除非“根”对象引用了所有其他对象。

否则,Apple 的“合并”会严重中断。

还...您可能需要手动“刺激”故障来完成这项工作;有一些关于这个的问题(我不知道为什么 Apple 不自动执行此操作 - 也许他们会这样做,但如果是这样,我还没有找到实现这一点的神奇选择)。

我认为还有其他一些警告。如果我记得它们,我稍后会编辑它。

注意:这听起来像是一大堆代码。是的,但是......事实证明,这比尝试通过字典等手动复制对象来遵循曲折的例子要少得多。

一旦你有了这个设置并开始工作,它在概念上很容易遵循。还...如果您执行上述所有步骤,Apple 将获得“大部分”正确的 NSNotifications。其余看起来不正确的(例如一些删除)是“如文档中所述”。它们对我来说没有意义,但至少它是这样记录的。

于 2010-07-05T12:04:12.967 回答
0

你的对象应该有一些唯一的标识符,比如唯一的整数 ID。这来自 Core Data 外部,取决于您的业务逻辑。所以当你从外部接收到一个新对象时,你检查具有这个 ID 的对象是否已经存在于 Core Data 中:如果是,你编辑现有的对象;如果不是,则添加新对象。

于 2010-07-04T02:50:56.517 回答