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, "条")
批量删除用 NSBatchDeleteRequest 同理。执行完要 mergeChanges 或重取数据,否则内存里的对象还是旧值,这是批量操作最常见的迷惑点。
索引与查询优化
查询慢往往是没有索引。给高频过滤、排序的字段在模型编辑器里勾上 Indexed,或者用 NSFetchIndexDescription 建组合索引。
let fetch: NSFetchRequest<Note> = Note.fetchRequest()
fetch.predicate = NSPredicate(format: "folder == %@ AND isArchived == false", folder)
fetch.sortDescriptors = [NSSortDescriptor(key: "updatedAt", ascending: false)]
fetch.fetchBatchSize = 100 // 分页取,别一次全拉
小数据量看不出差别,数据过十万后索引和 fetchBatchSize 就是生死线。能用 fetchLimit 就别取全量,能用属性(property)取个别字段就别取整对象。
多上下文与并发
写操作要放后台:创建私有上下文,perform 里做重活,保存后把变化 merge 回 viewContext。注意 Core Data 对象不是线程安全的,别跨上下文直接传对象,传 objectID。
let bg = container.newBackgroundContext()
bg.perform {
// 在这里批量导入、批量改
try bg.save()
DispatchQueue.main.async {
container.viewContext.mergeChanges(fromContextDidSave: Notification)
}
}
对象关系懒加载也可能引发跨上下文崩溃,改用 fetch 关联对象更安全。主上下文只在 viewContext 上做轻量操作,重活一律 backgroundContext,界面就不会卡。
小结
Core Data 进阶四件事:模型加版本保迁移、轻量迁移走默认、批量操作走存储层、索引分页控查询。多上下文加后台保存是并发标准姿势。把这几样练熟,Core Data 从"能存能取"变成"又快又稳"。