并发的神奇记录

Magical Record with concurrency

我在使用 Core Data 和 Magical Record 工作了一段时间后遇到错误,现在正在开发 iOS 应用程序:

error: NULL _cd_rawData but the object is not being turned into a fault

在这个项目之前我不知道 Core Data,事实证明我非常天真地认为我可以只使用 Magical Record 而不必担心并发性,因为我没有投入任何 thoughts/work关于主线程和后台线程的托管上下文。

在大量阅读有关 Core Data Managed Object Contexts 和 Magical Record 的文章后,我了解到:

关于我的申请的一些信息:

现在 - 我的问题是:

  1. 我应该只从 UI 线程中获取吗?我可以将托管对象保存在 DataModel 对象中吗?
  2. 如何从后台线程创建新实体并在 DataModel 对象中使用新创建的实体?
  3. 是否有我应该使用的最佳设计场景?特别是在向服务器发送请求并获得响应时,我是否应该创建一个新的托管上下文并在整个线程的 activity?
  4. 中使用它

如果一切都清楚,请告诉我。如果没有,我会尝试增加清晰度。

如有任何帮助或指南,我们将不胜感激。

谢谢!

我没有使用 MagicalRecord,但这些问题与 CoreData 的关系比与 MagicalRecord 的关系更密切,所以我会尽量回答它们:)。

1) 从主线程(UI) 获取

有很多方法可以设计应用程序模型,所以我使用 CoreData 几年来学到了两件重要的事情:

  • 在处理 UI 时,始终在主线程上获取对象。正如您正确指出的那样,NSManagedObjects 不是 线程安全的,因此您不能(好吧,可以,但不应该)从不同的线程访问它们的数据。 NSFetchedResultsController 是您最好的朋友,当您需要显示长列表时(例如,对于消息——但要注意请求的 batchSize)。

  • 您应该将存储和提取设计得快速且响应迅速。使用索引、只获取需要的属性、预取关系等

  • 另一方面,如果您需要从大量数据中获取数据,您可以在不同线程上使用上下文并仅传输 NSManagedObjectID。假设您的用户有大量消息,您希望向他显示来自特定联系人的最新 10 条消息。您可以创建后台上下文(私有并发),获取这 10 个消息 ID(NSManagedObjectIDResultType),将它们存储在数组(或任何其他适合您的格式)中,return 它们到您的主线程并获取那些仅 ID。请注意,如果由于 predicate/sortDescriptor 而需要很长时间获取,则此方法会加快速度,如果 "problem" 正在将故障转换为对象(例如,大 UIImage 存储在可转换属性中: ) )

2) 在后台创建实体

您可以在后台上下文中创建对象,保存它的 NSManagedObjectID 保存上下文后(对象在保存前只有临时 ID)并将其发送回您的主线程,其中您可以通过 ID 执行提取并在您的主要上下文中获取对象。

3) 使用背景上下文

我不知道它是否是最好的,但我对 NSManagedObjectContext 观察和通知合并非常满意。查看: mergeChangesFromContextDidSaveNotification:

因此,您创建后台上下文,添加主上下文作为更改的观察者 (NSManagedObjectContextObjectsDidChangeNotification),后台上下文会自动向您发送有关所有更改的通知(每次您执行保存时)– inserted/updated/deleted 对象(不用担心,您可以通过调用 mergeChangesFromContextDidSaveNotification: 来合并它)。这样有很多好处,比如:

  • 一切都会自动更新(您在 "observing context" 中获取的每个对象都会得到 updated/deleted)
  • 每次合并都在内存中运行(没有提取,没有在主线程上持久化)
  • 如果你实现 NSFetchedResultsController 的委托方法,一切都会自动更新(不完全是一切——见下文)

另一边:

  • 注意合并策略(NSMangedObjectContext mergePolicy)
  • 注意引用从后台(或另一个上下文)删除的托管对象
  • NSFetchedResultsController 仅在 "direct" 属性发生变化时更新(签出 this SO question

嗯,我希望它能回答你的问题。如果一切顺利,请毫不犹豫地询问 :)

关于子上下文的附注

还可以查看子上下文。他们也可以很强大。基本上每个子上下文都会在保存时将它的更改发送到父上下文(在 "base" 上下文(没有父上下文)的情况下,它会将它的更改发送到持久协调器)。

例如,当您创建 edit/add 控制器时,您可以从主上下文创建子上下文并在其中执行所有更改。当用户决定取消操作时,您只需销毁(删除引用)子上下文,不会存储任何更改。如果用户决定接受所做的更改 he/she,请保存子上下文并将其销毁。通过保存子上下文,所有更改都会传播到它的父存储(在本例中是您的主上下文)。只需确保还保存父上下文(在某个时候)以保留这些更改(保存:方法不执行 冒泡 )。签出 documentation of managing parent store.

编码愉快!