12

我有两个版本的模型Model001.xcdatamodelModel002.xcdatamodel. 这两个在Model.xcdatamodeld捆绑包中。我也有一个Model001to002.xcmappingmodel不属于Model.xcdatamodeld. 我检查了: xcmapingmodel 和 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];

这返回零。尽管我仔细检查了 Model001to002.cdm 是否在应用程序包中。它必须在应用程序包中,对吗?

4

5 回答 5

26

映射模型的“陷阱”是在创建映射后不允许对模型进行任何更改。如果这样做,您也会收到此错误。

于 2013-02-01T13:19:53.737 回答
6

好的,通过从 Xcode 中删除所有核心数据文件、读取它们并再次设置映射模型的源和目标来解决问题。

该死的Xcode!

于 2012-10-08T16:41:51.390 回答
4

创建映射模型后,您不能对源/目标模型进行任何更改。

如果您确实进行了一些更改,

  • mappingModelFromBundles:forSourceModel:destinationModel:将无法找到映射模型文件
  • addPersistentStoreWithType:configuration:URL:options:error:with{NSInferMappingModelAutomaticallyOption: @NO}会报错“Can't find mapping model for migration”
  • migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:会报错“映射和源/目标模型不匹配”

因此,只需重新创建映射模型并复制您在旧模型中所做的每一项更改。

于 2014-09-17T09:34:48.237 回答
2

TL;博士

至少从 Xcode 8/9 开始,打开映射模型,然后从Editor菜单中选择Refresh data models。通常,您似乎需要重新启动 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 进行了更改。如果您运行应用程序,迁移可能会成功,但不会使用您的自定义映射模型。

Remember if you are using a custom mapping, you will still need to use the Refresh Data Models technique before the mapping model will work.

于 2017-09-19T20:16:46.783 回答
0

如果您的测试设备的存储来自不再存在的数据模型版本,则可能会发生这种情况。

例如,我有数据模型版本 7,然后我制作了数据模型版本 8。我制作了一个从 7 到 8 的映射模型。然后我在我的测试设备上运行它,一切都很顺利。

然后我对 8 做了一些更改。

要意识到的是,在 Core Data 中,每个模型都有一个哈希标识符,系统通过获取 xcdatamodel 文件的校验和来创建该标识符。因此,即使您进行了细微的更改,即使您没有创建新版本,它也会将其视为不同的版本。这些版本的标识符是NSStoreModelVersionHashes(请参阅此处的文档)。

换句话说,我最终得到:

Data Model 7 (release) - 0plcXXRN7XHKl5CcF+fwriFmUpON3ZtcI/AfK748aWc=

Data Model 8 (beta)    - qeN1Ym3TkWN1G6dU9RfX6Kd2ccEvcDVWHpd3LpLgboI=

Data Model 8 (release) - EqtMzvRnVZWkXwBHu4VeVGy8UyoOe+bi67KC79kphlQ=

我没有制作版本 9,并将原始版本 8 保存在数据模型历史记录中,而是更新了 8,认为自动迁移可以解决我的问题。嗯,它不能,而且我不能在两者之间建立映射,因为 8 的旧(测试版)版本已经消失了。

我这样做是因为它是一个中间内部构建(不是发布),所以这没什么大不了的,但它确实让我陷入了循环!

如果它不是内部构建并且我需要完成这项工作,我可以返回(测试版)提交并拉出 8(测试版)的 xcdatamodel 文件,将(发布)版本重命名为 9,然后粘贴它进入发布版本并在 8 和 9 之间建立映射模型。

然而,由于它只是一个内部测试版,我们只是在测试设备上删除并重新安装了该应用程序。我们确实验证了,当从 7(发布)到 8(发布)时,迁移很顺利。

于 2016-07-06T02:55:59.737 回答