核心数据和并发性:

记住这一点:

“切勿在线程之间共享托管对象上下文。 这是一条硬规则,你不应该打破。”

并发性是同时处理多个队列上的数据的能力。

如果不了解多线程应用程序中的场景和用例,则对核心数据概念的了解将是不完整的。

老实说,Apple文档没有以简单的方式解释这些用例。 让我们从基础开始:

考虑以下两个问题:

如果从不同的线程访问相同的管理对象上下文,会发生什么情况?

如果将托管对象从后台线程传递到主线程会怎样?

Apple文档非常清楚Core数据希望在单线程上运行 ,而不必是主线程。 核心数据并非旨在从不同的线程访问。

但是,苹果公司的核心数据团队并不幼稚。 它知道需要从多个线程访问持久性框架。 单个线程(主线程)可能适合许多应用程序。 更复杂的应用程序需要一个健壮的多线程持久性框架。

在多线程应用程序中访问核心数据的基本规则:

托管对象:

NSManagedObject 实例绝不能从一个线程传递到另一个线程。 如果需要将托管对象从一个线程传递到另一个线程,请使用托管对象的 objectID 属性。 objectID 属性的类型为 NSManagedObjectID 并且唯一标识持久性存储中的记录。

当您将其交给NSManagedObjectID实例时,托管对象上下文知道该怎么做。

您需要了解三种方法:

  • object(with:)
  • existingObject(with:)
  • registeredObject(for:)
  • 第一个方法 object(with:) 返回与 NSManagedObjectID 实例 相对应的托管对象 如果托管对象上下文中没有该对象标识符的托管对象,它将询问持久性存储协调器。 此方法始终返回一个托管对象。
  • 知道 如果找不到接收到的对象标识符的记录 ,则 object(with:) 会引发异常。 例如,如果应用程序删除了与对象标识符相对应的记录,则Core Data无法将您的应用程序交给相应的记录。 结果是一个例外。
  • existingObject(with:) 方法的行为类似。 主要区别在于,如果该方法无法获取与对象标识符相对应的托管对象,则该方法将引发错误。
  • 第三种方法registeredObject(for:)仅在您要查询的记录已在托管对象上下文中注册的情况下才返回托管对象。 换句话说,返回值的类型为NSManagedObject? 。 如果托管对象上下文找不到,则不会从持久性存储中获取相应的记录。

记录的对象标识符与数据库记录的主键相似但不相同。 它唯一地标识记录,并使您的应用程序可以提取特定记录,而不管对该操作执行什么线程。

受管对象上下文:

创建 NSManagedObjectContext 实例是一种廉价的操作。 您永远不要在线程之间共享托管对象上下文。 这是一条硬规则,您不应该打破。 NSManagedObjectContext 类不是线程安全的。 干净利落。

持久性商店协调员:

即使 NSPersistentStoreCoordinator 类也不是线程安全的,即使多个托管对象上下文请求访问,该类也知道如何锁定自身,即使这些托管对象上下文在不同线程上运行并运行。 最好使用一个持久性存储协调器,该协调器可以由来自不同线程的多个托管对象上下文进行访问。 这使Core Data并发更加容易。

在复杂的大型应用程序中,您无法执行从NSManagedObjectContext到主线程上的持久性存储的保存操作。 因为它将阻塞主线程,直到未完成保存操作,并且可能导致该时间间隔内的应用程序冻结。

可以通过修改核心数据堆栈来避免这种情况,或者我可以说修改托管对象堆栈。

链接到持久性存储协调器的托管对象上下文与主线程无关。 相反,它在后台线程上运行并运行。 当私有管理对象上下文保存其更改时,将在该后台线程上执行写操作。

私有托管对象上下文具有子托管对象上下文,该子托管对象上下文用作应用程序的主要托管对象上下文。 在这种情况下,父级和子级管理对象上下文的概念是关键。

在大多数情况下,管理对象上下文与持久性存储协调器关联。 当此类受管对象上下文保存其更改时,会将其推入持久性存储协调器。 持久存储协调器将更改推送到持久存储,例如SQLite数据库。

子托管对象上下文没有对持久性存储协调器的引用。 而是保留对另一个托管对象上下文(父托管对象上下文)的引用。 当子托管对象上下文保存其更改时,会将其推入父托管对象上下文。 换句话说,当子托管对象上下文保存其更改时,持久性存储协调器不会意识到保存操作。 仅当父级受管对象上下文执行保存操作时,更改才被推送到持久性存储协调器,然后被推送到持久性存储。

由于在子受管对象上下文保存其更改时不执行任何写操作,因此执行该操作的线程不会被write操作阻塞这就是为什么应用程序的主要托管对象上下文是在后台线程上运行的托管对象上下文的子托管对象上下文。

参考:

https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/CoreData/Concurrency.html