Core Data 架构回顾

Core Data 的入口是 NSPersistentContainer,它把模型、存储、上下文都串起来。主上下文跑在 viewContext 上,后台任务用私有上下文,写入最终合并回主上下文。

上下文分层,是 Core Data 并发和性能的地基。

进阶之前先确认基础没塌:模型文件有没有版本管理、上下文有没有用对、保存有没有放在合适时机。基础不稳,后面的迁移和优化都会反复踩坑。

数据模型版本与轻量迁移

模型一改,旧用户的本地库就和代码不匹配,这时需要迁移。第一步永远是"加版本":选中 xcdatamodeld 文件,Editor 里 Add Model Version,把新版本设为当前。

let container = NSPersistentContainer(name: "App")
container.loadPersistentStores { _, error in
  // 开启轻量迁移:系统自动处理增删属性、加新实体等简单变化
}

加新属性(带默认值)、加新实体、删除属性,这些"轻量迁移"系统自动完成,几乎不用写代码。要注意:加了非可选的新属性却没默认值,就会迁移失败,记得给默认值。

复杂迁移与映射模型

把字段拆成两张表、两个实体合并成一个、给已有数据重算值,这些轻量迁移做不了,要写映射模型 Mapping Model:把源模型每个实体怎么变到目标模型写清楚。

let manager = NSMigrationManager(sourceModel: source, destinationModel: dest)
let mapping = NSMappingModel(from: nil,
  forSourceModel: source, destinationModel: dest)
try manager.migrateStore(from: srcURL, sourceType: NSSQLiteStoreType,
  options: nil, with: mapping, toDestinationURL: dstURL,
  destinationType: NSSQLiteStoreType, destinationOptions: nil)

复杂迁移先用小数据量在模拟器上测,再放线上。迁移耗时对老用户是体验损耗,能用轻量迁移就别上映射模型,能在启动后台做就别挡首屏。

批量操作与性能

一次性改一万条记录,逐个 fetch 再逐个赋值会慢到怀疑人生。NSBatchUpdateRequest 直接在存储层批量改,不经过内存里的对象图,速度快几个量级。

let batch = NSBatchUpdateRequest(entityName: "Note")
batch.propertiesToUpdate = ["isArchived": true]
batch.resultType = .updatedObjectsCountResultType
let result = try context.execute(batch) as? NSBatchUpdateResult
print("更新了", result?.result as? Int ?? 0, "条")
Core Data:发光数据库实体方块由连线连接成关系网络、底部带指针磁盘承载模型的蓝调插画
实体关系网络落在磁盘上。

批量删除用 NSBatchDeleteRequest 同理。执行完要 mergeChanges 或重取数据,否则内存里的对象还是旧值,这是批量操作最常见的迷惑点。