我有两个版本的模型Model001.xcdatamodel
和Model002.xcdatamodel
。这两个是Model.xcdatamodeld
捆绑。
我还有一个Model001to002.xcmappingmodel
,它不属于Model.xcdatamodeld
。我检查过:xcmappingmodel和xcdatamodeld都被复制到.app包中。
我的托管对象上下文初始化如下:
NSURL *documentModel = [bundle URLForResource:@"Model" withExtension:@"momd"]; managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:documentModel]; return managedObjectModel;
我还在我的initWithFileURL:
子类中覆盖UIManagedObject
上设置了这些属性。
NSMutableDictionary *options = [NSMutableDictionary dictionaryWithDictionary:self.persistentStoreOptions]; [options setObject:@YES forKey:NSMigratePersistentStoresAutomaticallyOption]; [options setObject:@YES forKey:NSInferMappingModelAutomaticallyOption]; self.persistentStoreOptions = [options copy];
但是当我尝试打开文档时,我收到以下错误:
Can't find mapping model for migration
- 更新 -
即使我进行手动迁移
[NSMappingModel mappingModelFromBundles:@[[NSBundle mainBundle]] forSourceModel:sourceObjectModel destinationModel:self.managedObjectModel];
这会返回nil。虽然我仔细检查了Model001to002.cdm是否在应用程序包中。它必须在应用程序包中吗?
答案 0 :(得分:20)
使用映射模型的“问题”是,在创建映射后,不允许对模型进行任何更改。如果这样做,您也会收到此错误。
答案 1 :(得分:3)
好的,通过从Xcode中删除所有核心数据文件,读取它们并再次设置映射模型的源和目标来解决问题。
该死的Xcode!
答案 2 :(得分:3)
创建映射模型后,不允许对源/目标模型进行任何更改。
如果你做了一些改变,
mappingModelFromBundles:forSourceModel:destinationModel:
将无法找到映射模型文件addPersistentStoreWithType:configuration:URL:options:error:
{NSInferMappingModelAutomaticallyOption: @NO}
将报告错误"无法找到迁移的映射模型" migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:
将报告错误"地图和源/目标模型之间的不匹配" 因此,只需重新创建映射模型并复制您在旧映射模型中所做的每一项更改。
答案 3 :(得分:0)
如果您的测试设备商店来自不再存在的数据模型版本,则会发生这种情况。
例如我有数据模型版本7,然后我制作了数据模型版本8.我制作了一个从7到8的映射模型。然后我在我的测试设备上运行它,一切都很开心。
然后我对8进行了一些更改。
要实现的是,在Core Data中,每个模型都有一个哈希标识符,系统通过获取xcdatamodel文件的校验和来创建。因此,即使您没有进行轻微更改,即使您没有创建新版本,也会将其视为不同的版本。这些版本'标识符为 NSStoreModelVersionHashes (请参阅文档here)。
换句话说,我最终得到了:
Data Model 7 (release) - 0plcXXRN7XHKl5CcF+fwriFmUpON3ZtcI/AfK748aWc=
Data Model 8 (beta) - qeN1Ym3TkWN1G6dU9RfX6Kd2ccEvcDVWHpd3LpLgboI=
Data Model 8 (release) - EqtMzvRnVZWkXwBHu4VeVGy8UyoOe+bi67KC79kphlQ=
而不是制作版本9,并将原始版本8保存在数据模型历史记录中,我刚刚更新了8,计算自动迁移可以照顾我。好吧,它不可能,而且我无法在两者之间进行映射,因为旧版(测试版)8已经不见了。
我是这样做的,因为它是一个中间内部构建(不是发布),所以它不是一个大问题,但它确实让我陷入了一个循环!
如果它不是内部构建而我需要来完成这项工作,我可以回到(beta)提交并将xcdatamodel文件拉出8(beta),将(发布)版本重命名为9,然后将其粘贴到发布版本中,并创建一个介于8和9之间的映射模型。
然而,由于它只是内部测试版,我们只是在测试设备上删除并重新安装了应用程序。我们确实验证了,当从7(发布)到8(发布)时,迁移进展顺利。
答案 4 :(得分:0)
至少从Xcode 8/9开始,打开映射模型,然后从编辑器菜单中选择刷新数据模型。通常看起来你需要重新启动Xcode。如果不这样做,您可以尝试重新选择模型编辑器底部的目标。
绝对从不在应用版本中分发模型后更改模型。
对于此示例,假设您已发布数据模型1(DM1)并正在迁移到DM2。如果您将DM2设置为活动版本然后运行您的应用程序,迁移将在您的持久存储上运行。如果您再对DM2进行另一项更改,请运行您的应用程序......轰!
问题是您的商店已经迁移到“DM2”,但商店中的数据不再适合模型。而且,我们无法再从DM2迁移到DM2。
继续创建DM3似乎是一个明显的解决方案。它是 通常最好尽量减少模型和数量 你正在发展中的迁移。
所以......现在你有一个持久的商店已经迁移到一个已经不存在的DM2。你如何再次测试迁移?您可以还原您的应用并使用DM1生成一些数据,但我更喜欢使用备份
在使用DM2运行应用程序之前,您可以复制现有商店(使用DM1)以用于以后的测试迁移。在macOS上,您可以轻松地手动执行此操作。下面的代码也可以解决问题。通常你不想发送它,而是你可以把它放在普通CD堆栈打开之前的某个地方,运行应用程序,然后停止应用程序(可能在通过Xcode结束运行之后放置一个断点)。
let fm = FileManager.default
let url = // The store URL you would use in ↓
// try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
let dir = url.deleteLastPathComponent().appendingPathComponent("Backup", isDirectory: true).appendingPathComponent("DM1", isDirectory: true)
print("Saving DB backup for DM1")
if !fm.fileExists(atPath: dir.path) {
do {
// Create a directory
try fm.createDirectory(at: dir, withIntermediateDirectories: true, attributes: nil)
let backupURL = dir.appendingPathComponent(url.lastPathComponent)
try fm.copyItem(at: url, to: backupURL)
}
catch {
print("Failed to save DB backup")
}
}
<噢>糟糕,我需要做出另一个改变......
如果您运行迁移到DM2,然后意识到您需要进行其他更改,则需要重新测试从DM1迁移 - > DM2。这是备份的来源。
进行备份的方式相同,运行此代码。
let fm = FileManager.default
let url = // The store URL you would use to add the store
let dir = url.deleteLastPathComponent().appendingPathComponent("Backup", isDirectory: true).appendingPathComponent("DM1", isDirectory: true)
let backupURL = dir.appendingPathComponent(url.lastPathComponent)
if fm.fileExists(atPath: backupURL.path) {
do {
fm.removeItem(at: url.path)
try fm.copyItem(at: backupURL, to: url)
}
catch {
print("Failed to restore DB backup")
}
}
您现在拥有已恢复的DM1商店,并对DM2进行了更改。如果您运行该应用程序,迁移可能会成功,但它不会使用您的自定义映射模型。
请记住,如果您使用的是自定义映射,则在映射模型工作之前,您仍需要使用刷新数据模型技术。