还记得在之前所有 core data 学习过程中我们使用的 managedContext 以及在背后默默工作的 CoreDataManager.swift 么?对 core data 的操作有了足够多了解之后,现在,是时候说说它们究竟是什么了。

实际上,为了尽可能保持核心简单,并提供灵活的扩展性,Core data 是通过 4 个不同的类为我们提供服务的:

  • NSManagedObjectModel
  • NSPersistentStore
  • NSPersistentStoreCoordinator
  • NSManagedObjectContext

这些类形成的工作模式,叫做 core data stack。而我们一直使用的 CoreDataManager,就是一个自定义的 core data stack。

NSManagedObjectModel

首先,我们从 NSManagedObjectModel 说起,它的作用类似数据库中的 schema,描述了 Core Data 中保存了哪些 ENTITY,有哪些属性,有哪些关系等。之前,我们在 Xcoode 的 Core Data 编辑器中定义了这些内容,那么这些内容是如何与 NSManagedObjectModel 对应起来的呢?实际上,当构建整个项目的时候,Xcode 会使用一个叫做 momc 的工具,对项目中的 .xcdatamodeld 文件进行处理,并把处理后的结果放在构建目录中的一个叫做 momd 的子目录中:

在运行时,Core Data 就会根据这个结果创建 NSManagedObjectModel 对象了。在 CoreDataManager.swift 中,可以找到下面这段代码:

let dataKitBundle = Bundle.main
let modelUrl = dataKitBundle.url(
  forResource: self.model, withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOf: modelUrl)!

它执行的逻辑,对应的就是刚刚我们介绍的创建 NSManagedObjectModel 的过程。至于到底怎么用这个 managedObjectModel,我们看完全部的 Core Data stack 就清楚了,现在只要知道它是什么就好。

NSPersistentStore

其次,是 NSPersistentStore,它表示 Core Data 的数据保存方式。例如我们一直以来使用的 SQLite。实际上,Core Data 一共提供了四种不同的方式:

  • NSSQLiteStoreType:这就是一直以来我们使用的默认的用 SQLite 保存数据;对于绝大多数 App 来说,它也是最好的选择;
  • NSXMLStoreType:表示使用 XML 保存 Core Data 的数据。这种方式只有在 macOS 上才能使用;
  • NSBinaryStoreType:表示使用一个二进制文件保存 Core Data 的数据。在实际的应用中,我们很少见到这种方式;
  • NSInMemoryStoreType:表示仅仅在内存中保存 Core Data 的数据,因此,这不是一个严格意义上的数据持久化选项。在单元测试中,我们经常使用这种存储类型;

对于这四种不同的类型,它们的工作方式还可以分成两大类,第一类叫做 atomic 类型:表示需要把保存 Core Data 的全部数据加载到内存中后,才可以执行 Core Data 的各种操作。除了 NSSQLiteStoreType 之外,其余的三种类型都是 atomic 类型,因此,当数据量大了之后,使用这些方式会占用大量的内存空间。第二类当然就是 non-atomic,它可以有选择的加载要操作的数据进内存,这也是为什么 Core Data 默认会使用 NSSQLiteStoreType 类型的原因。

看到这,你可能会想了,我在什么地方指定这个类型呢?别着急,等介绍完 Core Data stack 之后,我们再来看它的用法。

NSPersistentStoreCoordinator

下一个要介绍的类型,是 NSPersistentStoreCoordinator。就如同这个类型的名字一样,它是在 NSManagedObjectModelNSPersistentStore 之间的一层媒介。因此,一方面,它可以从 NSManagedObjectModel 了解到 Core Data 要保存的数据类型和关系;另一方面,它也可以通过 NSPersistentStore 把数据真正持久化到合适的目标。这样做有两个好处:

  • 一方面,我们一直使用的 NSManagedObjectContext 可以不关心具体的数据保存方式,在它眼里,只有一个由 NSPersistentStoreCoordinator 提供的持久化数据的接口;
  • 另一方面,我们还可以通过 NSPersistentStoreCoordinator 把同一份数据持久化到多个不同的存储媒介;

NSManagedObjectContext

最后一个要介绍的类型,是 NSManagedObjectContext,实际上我们已经和它打很多次交到了。这是 Core Data 对外提供服务的窗口。它为 Core Data 中的数据提供了一层内存缓存,因此我们可以安全地对其中的数据进行各种操作,直到调用 save 方法的时候,才会把缓存中的数据写回 Core Data。

正是因为这个特性,在之前我们创建各种 NSManagedObject 对象的时候,总是要在 init 方法中传入一个 NSManagedObjectContext 对象:

let series = Series(context: managedContext)
let episode1 = Episode(context: managedContext)
let history1 = History(context: managedContext)

一旦创建好了,这个对象就只能“存活”在这个上下文对象中了。另外要说一下的是,这个上下文对象并不是线程安全的,NSManagedObject 对象的操作只能在创建这个对象的线程中完成。当然 Apple 也提供了在多线程环境中使用上下文环境的办法,不过这不是我们现在的主题,等真正用到这种场景的时候,我们再来详细讨论它的用法。

至此,Core Data stack 中涉及的四个类型,我们就都说完了。把它们的关系结合之前我们创建的 ENTITY 用一张图表示,就是这样的:

可以看到,NSPersistentStoreCoordinator 是整个结构的枢纽,它从我们创建的 Core Data 模型中了解数据的内容和关系,根据这些知识把数据从 NSPersistentStore 读到 NSManagedObjectContext 供我们操作,而我们也通过它把数据写回到各种存储里。

What's next?

说了这么多,究竟应该如何创建一个 NSManagedObjectContext 对象呢?一直以来,我们的例子中都在使用一个预先创建好的 managedContext 对象。在早期的 Core Data 版本里,这个过程有点儿烦琐。就像 Core Data stack 这个名字一样,为了创建。NSManagedObjectContext,我们需要创建一个 NSPersistentStoreCoordinator。为了创建 NSPersistentStoreCoordinator,我们又需要创建一个 NSManagedObjectModel。是不是听着就很啰嗦?

好在 iOS 10 之后,Core Data 中多出来了一个叫做 NSPersistentContainer 的类型。就如同它的名字一样,它可以把 Core Data stack 中相关的类型都包含进来,就不用再像之前一样一层层的创建对象了。我们只需要创建一个容器对象,用它加载 Core Data 的本地存储就完事儿了。下一节,借着 NSPersistentContainer,我们来看在项目中使用的 CoreDataManager 的实现,实际上,它和我们在泊学 App 中用到的 Core Data 管理类,是完全一样的。

所有订阅均支持 12 期免息分期

¥ 59

按月订阅

一个月,观看并下载所有视频内容。初来泊学,这可能是个最好的开始。

开始订阅

¥ 512

按年订阅

一年的时间,让我们一起疯狂地狩猎知识吧。比按月订阅优惠 28%

开始订阅

¥ 1280

泊学终身会员

永久观看和下载所有泊学网站视频,并赠送 100 元商店优惠券。

我要加入
如需帮助,欢迎通过以下方式联系我们