这篇笔记主要是介绍一些 CoreData 的基本概念。Core Data 是苹果提供的数据持久化方案。相对于 Sqlite,CoreData 提供了原生的 ORM 的支持,以及可以和苹果的 Cloud 服务进行通信,对于独立开发者来说,这是成本很低的数据存储方案,目前也是很多开发者使用的技术栈。
Core Data 我自己感觉稍微有一定的使用门槛,不过苹果这几年也在不断地降低CoreData API 和相关概念的使用和理解难度,这部分的技术储备还是要进行的。
我尝试从 CoreData Stack 这里开始理清 CoreData 相关的比较难理解的概念。事实上也就 CoreData Stack 这里比较难理解。
# CoreData Stack
CoreData Stack 就是一组 framework 对象,这组对象作为 CoreData 初始化部分以及用于管理在应用内对象和外部数据源交互。CoreData Stack 负责所有和外部数据源打交道工作,这样我们业务方只需要关心自己的业务逻辑就可以了。CoreData Stack 主要包含四种对象
- NSManagedObjectContext
- NSPersistentStoreCoordinator,
- NSManagedObjectModel
- NSPersistentContainer
接下来一个个说下这几个关键对象
- NSManagedObjectModel 👉 这个
NSManagedObjectModel
实例描述了被 CoreData Stack 访问的数据。其实就是用程序化的方式描述了 .xcdatamodeld 里的对象。当开始初始化 Core Data stack 的时候,数据库文件最先被加载进内存和NSManagedObjectModel
形成映射关系,系统当然知道要加载的文件的名字。 - NSPersistentStoreCoordinator 👉 位于 CoreData Stack 的中间层,负责将定义在 model 里的实体实例化,也可以从持久化存储(
NSPersistentStore
)里获取实例。所以它的职责就是根据NSManagedObjectModel
的定义,从持久化存储里取出或者创造实例对象,并且将这些对象传给NSManagedObjectContext
. 同时也会校验数据的一致性?(这个不是很明白) - NSManagedObjectContext 👉 这个对象是我们应用调用最多的,应用就是通过它从持久化存储(persistent store)获取一份临时的数据,然后应用可以任意改变这份数据,如果你不主动调save方法,那持久化存储里的数据是保持不变的。我们就是通过这个 context 来对数据进行增删等操作,这个 context 也会追踪我们做了哪些操作,这样它就可以试下undo/redo 这些操作了。而当我们主动调 save 方法的时候,context 会确保我们的数据是有效的,有效的话就直接写回到持久化存储里面。
# CoreData Stack 初始化
我们只能在初始化 CoreDataStack 栈之后才能在应用中访问外部数据源,下面这几行代码就是 CoreDataStack 的创建:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "CoreDataDemo")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
NSLog(@"Failed to load Core Data stack: %@", error);
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
loadPersistentStores(completionHandler:) API 说明:Once the persistent container has been initialized, you need to execute
loadPersistentStores(completionHandler:)
to instruct the container to load the persistent stores and complete the creation of the Core Data stack. Once the completion handler has fired, the stack is fully initialized and is ready for use. The completion handler will be called once for each persistent store that is created.
可以看到所谓 CoreData Stack 的初始化其实就是创建了一个 NSPersistentContainer
,然后去加载了本地的持久化存储。
本质上 NSPersistentContainer
的使用其实是把 CoreData Stack 的比较复杂的流程封装起来了,当我们创建 NSPersistentContainer
的时候,本质上也创建了 NSManagedObjectModel
,NSPersistentStoreCoordinator
以及 NSManagedObjectContext
。
在 iOS10 之前还有一种比较原始的的 CoreData Stack 初始方式,在这种方式里,你可以比较清楚的看到 CoreData Stack 里的对象是如何协作的。
init(completionClosure: @escaping () -> ()) {
//1. 初始化 NSManagedObjectModel.
//2. 通过 NSManagedObjectModel 初始化 NSPersistentStoreCoordinator
//3. 通过 NSPersistentStoreCoordinator 初始化 NSManagedContext.
//This resource is the same name as your xcdatamodeld contained in your project
guard let modelURL = Bundle.main.url(forResource: "DataModel", withExtension:"momd") else {
fatalError("Error loading model from bundle")
}
// The managed object model for the application. It is a fatal error for the application not to be able to find and load its model.
guard let mom = NSManagedObjectModel(contentsOf: modelURL) else {
fatalError("Error initializing mom from: \(modelURL)")
}
//
let psc = NSPersistentStoreCoordinator(managedObjectModel: mom)
managedObjectContext = NSManagedObjectContext(concurrencyType: NSManagedObjectContextConcurrencyType.mainQueueConcurrencyType)
managedObjectContext.persistentStoreCoordinator = psc
//
let queue = DispatchQueue.global(qos: DispatchQoS.QoSClass.background)
queue.async {
guard let docURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last else {
fatalError("Unable to resolve document directory")
}
let storeURL = docURL.appendingPathComponent("DataModel.sqlite")
do {
try psc.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: storeURL, options: nil)
//The callback block is expected to complete the User Interface and therefore should be presented back on the main queue so that the user interface does not need to be concerned with which queue this call is coming from.
DispatchQueue.main.sync(execute: completionClosure)
} catch {
fatalError("Error migrating store: \(error)")
}
}
}
但是上面这些创建流程看起来实在复杂,于是出现了 NSPersistentContainer
来简化 CoreData Stack 的初始化流程。NSPersistentContainer
也提供了配置类 NSPersistentStoreDescription
来简化 CoreData 的配置,我们在后面会不断看到 CoreData 的配置。
参考地址:
- Core Data Programming Guide-Initializing the Core Data Stack (opens new window)
- 肘子的Swift记事本-掌握 Core Data Stack (opens new window)
相关链接: