对于第一次接触 Core Data 的人来说,这是一个极其令人困惑的话题。我并不是轻率地这么说,但根据经验,我有信心说苹果文档在这个问题上有些误导(如果你仔细阅读的话,实际上是一致的,但它们没有充分说明为什么合并数据仍然存在)在许多情况下,这是比依赖父/子上下文并简单地从子项保存到父项更好的解决方案)。
该文档给人留下了强烈的印象,父/子上下文是进行后台处理的新首选方式。然而,苹果公司却忽略了一些强烈的警告。首先,请注意,您获取到子上下文中的所有内容都首先通过其父上下文拉取。因此,最好限制在主线程上运行的主上下文的任何子级来处理(编辑)已在主线程上的 UI 中呈现的数据。如果您将其用于一般同步任务,您可能需要处理远远超出当前 UI 中显示范围的数据。即使您使用 NSPrivateQueueConcurrencyType,对于子编辑上下文,您也可能会在主上下文中拖动大量数据,这可能会导致性能不佳和阻塞。现在最好不要使主上下文成为用于同步的上下文的子上下文,因为除非您要手动执行此操作,否则它不会收到同步更新的通知,而且您将在某个计算机上执行可能长时间运行的任务。您可能需要对从作为主上下文子级的编辑上下文、通过主要联系人一直到数据存储的级联启动的保存做出响应。您必须手动合并数据,还可能跟踪需要在主上下文中失效并重新同步的内容。这不是最简单的模式。
Apple 文档没有明确说明的是,您很可能需要混合描述“旧”线程限制处理方式的页面上描述的技术和新的父子上下文处理方式。
您最好的选择可能是(我在这里给出一个通用解决方案,最佳解决方案可能取决于您的详细要求),将 NSPrivateQueueConcurrencyType 保存上下文作为最顶层的父级,直接保存到数据存储区。 [编辑:您不会直接在此上下文上做太多事情],然后为该保存上下文提供至少两个直接子级。一个是用于 UI 的 NSMainQueueConcurrencyType 主上下文 [编辑:最好遵守纪律,避免在此上下文中对数据进行任何编辑],另一个是 NSPrivateQueueConcurrencyType,您用于对数据进行用户编辑,并且(在附图中的选项 A)您的同步任务。
然后,将主上下文作为同步上下文生成的 NSManagedObjectContextDidSave 通知的目标,并将通知 .userInfo 字典发送到主上下文的 mergeChangesFromContextDidSaveNotification: 。
下一个要考虑的问题是在哪里放置用户编辑上下文(用户所做的编辑将反映回界面的上下文)。如果用户的操作始终仅限于对少量呈现数据进行编辑,那么使用 NSPrivateQueueConcurrencyType 再次将其作为主上下文的子级是您的最佳选择,并且最容易管理(保存将直接将编辑保存到主上下文中,并且如果如果您有一个 NSFetchedResultsController,则会自动调用适当的委托方法,以便您的 UI 可以处理更新控制器:didChangeObject:atIndexPath:forChangeType:newIndexPath:)(这又是选项 A)。
另一方面,如果用户操作可能导致处理大量数据,您可能需要考虑将其设为主上下文和同步上下文的另一个对等体,以便保存上下文具有三个直接子级。main, sync(私有队列类型)和edit(私有队列类型)。我在图中将这种安排显示为选项 B。
Similarly to the sync context you will need to [Edit: configure the main context to receive notifications] when data is saved (or if you need more granularity, when data is updated) and take action to merge the data in (typically using mergeChangesFromContextDidSaveNotification:). Note that with this arrangement, there is no need for the main context to ever call the save: method.
要理解父/子关系,请采用选项 A:父子方法简单地意味着如果编辑上下文获取 NSManagedObjects,它们将首先被“复制到”(注册)保存上下文,然后是主上下文,最后是编辑上下文。您将能够对它们进行更改,然后当您在编辑上下文中调用 save: 时,更改将被保存只是到主要上下文。您必须在主上下文上调用 save: ,然后在保存上下文上调用 save: ,然后才能将它们写入磁盘。
当您从子级保存到父级时,会触发各种 NSManagedObject 更改和保存通知。例如,如果您使用获取结果控制器来管理 UI 的数据,那么将调用它的委托方法,以便您可以根据需要更新 UI。
一些后果:如果您在编辑上下文中获取对象和 NSManagedObject A,然后修改它并保存,因此修改将返回到主上下文。现在,您已针对主上下文和编辑上下文注册了修改后的对象。这样做是不好的风格,但您现在可以在主上下文中再次修改该对象,并且它现在将与存储在编辑上下文中的对象不同。如果您随后尝试对存储在编辑上下文中的对象进行进一步修改,则您的修改将与主上下文中的对象不同步,并且任何保存编辑上下文的尝试都会引发错误。
因此,使用像选项 A 这样的安排,尝试获取对象、修改它们、保存它们并重置编辑上下文是一个很好的模式(例如,使用运行循环的任何单次迭代(或在传递给 [editContext PerformBlock:]) 的任何给定块。最好还是遵守纪律并避免这样做any对主要上下文的编辑。
另外,重申一下,由于 main 上的所有处理都是主线程,因此如果您将大量对象提取到编辑上下文,则主上下文将执行提取处理在主线程上因为这些对象被迭代地从父上下文复制到子上下文。如果正在处理大量数据,这可能会导致 UI 无响应。因此,例如,如果您有大量托管对象,并且您有一个 UI 选项,将导致它们全部被编辑。在这种情况下,像选项 A 那样配置您的应用程序是一个坏主意。在这种情况下,选项 B 是更好的选择。
如果您不处理数千个对象,那么选项 A 可能就完全足够了。
顺便说一句,不必太担心您选择的选项。如果您需要更改为 B,那么从 A 开始可能是个好主意。进行此类更改比您想象的要容易,而且通常产生的后果也比您预期的要少。