【问题标题】:How to make/use temporary NSManagedObjects?如何制作/使用临时 NSManagedObject?
【发布时间】:2011-03-11 13:13:17
【问题描述】:

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

  1. 加载核心数据存储
  2. 下载新数据,创建对象 在记忆中
  3. 部分新数据保存到 存储(通常“只有新的比特/没有改变的比特”)

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

  1. 不要在内存中创建对象 (嗯,这意味着扔掉 关于对象的一切都很好。 使用大量代码编写代码 NSDictionary 没有任何目的 除了解决CoreData的 失败。一般不可用)
  2. 创建对象,然后删除 你不想要的(苹果建议 这在文档中,但他们的通知非常糟糕 错误:那些“删除”出现在 你试图保存,即使他们 不应该/不能)
  3. 在辅助中创建对象 上下文(Apple 强烈暗示这一点 是正确的,但显然没有提供任何方法 你从临时移动对象 真实的上下文,没有 执行上述操作(删除对象 你刚刚创建,然后做一个 保存)。这通常是不可能的,因为对象通常需要连接到新上下文中的引用,并且保存会失败)

当然,应该没这么难吧?

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

到目前为止,我工作的唯一解决方案是选项 2(来自苹果的文档),当 Apple 发送 NSNotifications 时,Apple 发送的对象本来不应该被保存(但 Apple 发送无论如何通知)。这是一个可怕的黑客攻击。

编辑:澄清:

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

【问题讨论】:

    标签: iphone core-data nsmanagedobjectcontext


    【解决方案1】:

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

    【讨论】:

    • 一般来说,在创建对象之前,您永远不会知道这些数据。这是标准的 OOP:首先分配/初始化,然后填写剩余数据。例如,XML 解析:您很少知道唯一 ID,直到您至少解析了一些对象(例如查看 RSS:如果 ID 是父节点上的一个属性,您的建议会起作用;相反, ID 嵌入在子节点的深处)。
    • 嗯...您不是直接以 Core Data 格式下载数据。我自己使用的模式是将数据作为 JSON 获取,并且可以将该 JSON 解析为 NS* 对象,例如 NSDictionary。我可以从字典中获取 ID,然后决定是创建一个新的 Core Data 对象还是将这个数据与现有对象关联。
    【解决方案2】:

    看来选项 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。其余看起来不正确的(例如一些删除)是“如文档中所述”。它们对我来说没有意义,但至少它是这样记录的。

    【讨论】:

    • 修改:在 OS 4 上,在一些极少数情况下在 OS 3 上,这效果很差,除非你使用 NSOperationQueue 而不是 performSelectorInBackground(线程使用队列更平滑 - 在 OS 3 上它可以防止前台线程从饿死,在 OS 4 上我不确定发生了什么,但感觉更顺畅)
    • 另外:我遇到的一些“合并”问题是因为我没有发现(大部分未记录的)[NSManagedObject prepareForDeletion] 方法;在 99% 的情况下,您必须覆盖应用程序中每个对象的该方法(Apple 从未在文档中提及这一点),以便从 Apple 获得有效的合并通知。否则,Apple 会在向您发送通知之前删除您处理合并所需的数据!
    【解决方案3】:

    有 3 种常见的处理方法。

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

      (如果您关心 I/O,可以使用内存中的上下文,这有利有弊。)

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-30
      • 2011-12-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多