找不到用于迁移的映射模型 - UIManagedDocument核心数据迁移

时间:2012-10-08 13:16:30

标签: objective-c ios cocoa-touch core-data xcode4.5

我有两个版本的模型Model001.xcdatamodelModel002.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是否在应用程序包中。它必须在应用程序包中吗?

5 个答案:

答案 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)

TL; DR

至少从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进行了更改。如果您运行该应用程序,迁移可能会成功,但它不会使用您的自定义映射模型。

  

请记住,如果您使用的是自定义映射,则在映射模型工作之前,您仍需要使用刷新数据模型技术。