如何制作/使用临时NSManagedObjects?

时间:2010-07-04 02:12:01

标签: iphone core-data nsmanagedobjectcontext

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

  1. 加载coredata商店
  2. 下载新数据,创建对象 在记忆中
  3. 部分新数据保存到 存储(通常“只有新位/未更改的位”)
  4. 相反,我可以找到这些替代品,其中没有一个是正确的:

    1. 不要在内存中创建对象 (好吧,这意味着扔掉 关于物体的一切都好。 使用大量代码编写代码 NSDictionary的人没有任何意义 除了解决方案CoreData的问题 缺点。一般不可行)
    2. 创建对象,但随后删除 你不想要的(苹果建议 这在文档中,但他们的通知可怕 错误:那些“删除”出现在何时 你试图保存,即使他们 不应该/不能)
    3. 在辅助设备中创建对象 上下文(Apple强烈暗示这一点 是正确的,但显然没有提供任何方法 你要从临时移动对象 真实的背景,没有 做上面的事情(删除对象 你刚刚创建,然后做了一个 保存)。这通常是不可能的,因为对象通常需要连接到新上下文中的引用,并且保存将失败)
    4. 当然,这不应该这么困难吗?

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

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

      编辑:澄清:

      我无法弄清楚如何正确传递Apple的通知。 Apple的代码似乎将插入转换为“更新”,并将“临时对象”转换为“删除”等。我无法听取“新对象”。

3 个答案:

答案 0 :(得分:2)

有三种常见方法可以解决这个问题。

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

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

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

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

  4. #1和#3的优点是你的对象生活在CoreData世界中 - 例如,它们可以被查询,它们可以参与关系等。#2的优点是它轻快,特别是如果你有很多临时物品。

答案 1 :(得分:1)

似乎选项3是最佳选择。

编辑:在iOS 4上广泛使用之后,我会说“总是使用NSOperationQueue而不是performSelectorOnBackgroundThread”。如果您不知道如何使用NSOpQ,那么谷歌就可以了,但是可以用少于3行代码完成,所以使用performSel只需要很小的改动。使用iOS4的新线程调度程序,它可以更好地工作

基于“我怎么能强迫这个?”,我想出了这个方法:

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

    否则,Apple的“合并”会严重破坏。

    另外......你可能需要手动“刺激”错误来完成这项工作;有一些关于这一点的问题(我不知道为什么Apple不会自动执行此操作 - 也许他们会这样做,但如果是这样的话,我还没有找到让这种情况发生的神奇选项。)

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

    注意:这听起来像是很多代码。是的,但是......结果比使用字典等手动复制对象的曲折例子要少得多。

    一旦你有了这个设置和工作,它在概念上很容易遵循。另外......如果您完成所有上述步骤,Apple会将“大多数”NSNotifications正确化。其余看起来不正确的(例如一些删除)是“如文档中所述”。它们对我没有意义,但至少它是如何记录下来的。

答案 2 :(得分:0)

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