这一节,我们说说项目一直在背后默默服务的 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 时和记录约束发生冲突时的问题。

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

¥ 59

按月订阅

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

开始订阅

¥ 512

按年订阅

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

开始订阅

¥ 1280

泊学终身会员

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

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