这一节,我们说说项目一直在背后默默服务的 CoreDataManager
。它对 Core Data 中常用的对象和操作,提供了一层封装,这样,就不用反复去写一些模板代码了。
NSPersistentContainer
首先,我们从上一节末尾提到的 NSPersistentContainer
对象开始。它是 CoreDataManager
中的一个 lazy
属性:
public lazy var persistentContainer: NSPersistentContainer = {
/// 1. Create `NSPersistentContainer`
let dataKitBundle = Bundle.main
let modelUrl = dataKitBundle.url(
forResource: self.model, withExtension: "momd")!
let managedObjectModel = NSManagedObjectModel(contentsOf: modelUrl)!
let container = NSPersistentContainer(
name: self.model, managedObjectModel: managedObjectModel)
/// 2. Load configuration
container.loadPersistentStores(completionHandler: {
(storeDescription, error) in
if let error = error {
fatalError("Cannot load core data store.")
}
})
container.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
return container
}()
可以看到,在第一部分,创建 NSPersistentContainer
的时候,我们只是给它传递了表达数据关系的 NSManagedObjectModel
就好了。第二部分,通过 loadPersistentStores
方法,我们可以对 Core Data 使用的存储进行设置。completionHandler
的第一个参数,是一个 NSPersistentStoreDescription
对象,要设置上一节提到过的 NSPersistentStore
,就可以这样:
container.loadPersistentStores(completionHandler: {
(storeDescription, error) in
storeDescription.type = NSInMemoryStoreType // In memory store
})
另外,我们还设置了当 viewContext
中的内容和 Core Data 中记录的约束发生冲突时的处理策略,关于 NSMergeByPropertyObjectTrumpMergePolicy
,在下一节我们专门来演示它的用法,现在只要大概知道它的功能就好了。
NSManagedObjectContext
有了 persistentContainer
之后,要获取面向用户操作的 NSManagedObjectContext
就很简单了。访问它的 viewContext
属性就好。鉴于这是一个 Core Data 中非常常用的对象,我们也为它在 CoreDataManager
中单独定义了一个属性:
public lazy var managedContext: NSManagedObjectContext = {
self.persistentContainer.viewContext
}()
而这,就是在之前介绍 Core Data 基本功能时,一直使用的 managedContext
。不过,这里要提前说一下的是,在很多情况下,我们都需要使用不同的上下文对象,特别是在多线程环境里。因此,不要把 managedContext
的这种用法作为一个固定的使用上下文对象的方式。稍后在实现泊学 App 通过网络缓存数据的功能时,我们会专门提到 Core Data 在多线程环境中的用法。现在,只要对这个事情有个印象就好了。
把数据保存到 Core Data
接下来,是把 context 中的数据保存到 Core Data 的方法。在介绍 CoreDataManager
之前,我们都是直接使用的 managedContext.save()
方法。实际上,这个操作可以更有效率一些。就像 saveContext
的实现一样,在真正保存之前,可以先判断下 managedContext.hasChanges
的值。只有在发生改动之后,再真正调用 save
方法保存记录。
public func saveContext() {
guard managedContext.hasChanges else { return }
do {
try managedContext.save()
}
catch let error as NSError {
fatalError("Cannot save core data. Error: \(error), \(error.userInfo)")
}
}
从 Core Data 删除数据
最后,是从 Core Data 中删除记录的方法 clearStorage
:
public func clearStorage(entityName: String) {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(
entityName: entityName)
let batchDeleteRequest = NSBatchDeleteRequest(
fetchRequest: fetchRequest)
do {
try managedContext.execute(batchDeleteRequest)
}
catch let error as NSError {
print("Cannot delete local storage for: \(entityName).\nReason: \(error.localizedDescription)")
}
}
之前我们做的,都是删除 Core Data 中的某一条记录。如果要清空某个 ENTITY 中的所有记录,我们可以先用 NSFetchRequestResult
创建一个 NSFetchRequest
对象,表示要请求 entityName
中的所有记录。然后,把这个请求传递给 NSBatchDeleteRequest
,转化成一个删除对应记录的请求。再把这个请求传递给 managedContext.execute
方法执行,这样 entityName
指定的 ENTITY 中的所有记录,就都被清空了。在单元测试中,这个方法往往会非常有用。
What's next?
了解了 CoreDataManager
的实现之后,下一节,我们分享一个简单但非常常用的设置,如何给 Core Data 中的记录添加约束。以及如何处理 NSManagedObjectContext
中的内容在写回 Core Data 时和记录约束发生冲突时的问题。