第6章:Schema和版本迁移
自定义迁移 (Custom Migration)
何时需要自定义迁移
当数据模型发生以下复杂变更时,轻量级迁移无法满足需求,需使用自定义迁移:
- 属性类型发生不兼容的变更(如
String改为Int) - 需要执行数据清洗或转换逻辑(如拆分合并字段)
- 涉及非标准关系调整(如一对多改为多对多)
实现自定义迁移的步骤
创建映射模型(Mapping Model)
# 在Xcode中: File -> New -> File -> Mapping Model # 选择源版本和目标版本数据模型编写迁移策略类
class CustomMigrationPolicy: NSEntityMigrationPolicy { // 属性转换示例:将fullName拆分为firstName/lastName @objc func transformFullName(forInsertion: String) -> [String: String] { let components = forInsertion.components(separatedBy: " ") return ["firstName": components.first ?? "", "lastName": components.last ?? ""] } }配置迁移选项
let container = try ModelContainer( for: Book.self, migrationPlan: MyMigrationPlan.self, configurations: ModelConfiguration( migrationPlan: MyMigrationPlan.self ) )
核心API与工具
| 工具/API | 用途 |
|---|---|
NSMigrationManager | 管理迁移过程的核心类 |
NSEntityMigrationPolicy | 实现自定义迁移逻辑的基类 |
NSMappingModel | 描述源模型与目标模型之间的映射关系 |
modelConfiguration | 配置迁移时使用的模型版本 |
调试迁移过程
启用详细日志:
UserDefaults.standard.set(true, forKey: "com.apple.CoreData.SQLDebug")常见错误处理:
错误:"Can't find mapping model for migration"
- 检查Mapping Model是否添加到编译目标
- 验证源/目标模型版本选择是否正确
错误:"Migration failed due to missing source instance"
- 检查实体/属性名称拼写
- 验证
NSEntityMigrationPolicy的方法签名
最佳实践
- 分阶段迁移:对于大型变更,拆分为多个小版本逐步迁移
- 数据备份:迁移前确保有完整数据备份
- 测试策略:
// 测试环境使用内存存储验证迁移 let testContainer = ModelContainer( for: Schema([Book.self]), configurations: ModelConfiguration(inMemory: true) ) - 性能优化:
- 批量处理数据(每次迁移1000条左右记录)
- 禁用UI更新直到迁移完成
完整示例:书籍出版日期格式迁移
// 旧模型:出版日期为String类型(格式:"YYYY-MM-DD")
// 新模型:出版日期为Date类型
class BookDateMigrationPolicy: NSEntityMigrationPolicy {
private static let dateFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd"
return formatter
}()
@objc func transformToDate(forInsertion: String) -> Date? {
return Self.dateFormatter.date(from: forInsertion)
}
}
注意:自定义迁移完成后,建议运行数据完整性检查,验证所有关键数据是否迁移成功。
