核心数据迁移问题:“持久存储迁移失败,缺少源托管对象模型。”
Posted
技术标签:
【中文标题】核心数据迁移问题:“持久存储迁移失败,缺少源托管对象模型。”【英文标题】:Core Data migration problem: "Persistent store migration failed, missing source managed object model." 【发布时间】:2010-03-22 12:19:48 【问题描述】:背景
可可非文档核心数据 具有两个托管对象的项目 楷模。 模型 1 保持不变。模型 2变了,所以想迁移 商店。 我创建了一个新版本 按设计 > 数据模型 > 添加模型 Xcode 中的版本。 版本之间的区别在于单一关系已从一对多变为一对多。 我做了我的 对模型所做的更改,然后保存。 我制作了一个新的映射模型, 有旧模型作为来源和新的 模型作为目的地。 我保证 所有映射模型和数据模型 并且正在编译,所有都是 复制到我的资源文件夹 应用程序包。 我已通过以下方式开启迁移 传入字典NSMigratePersistentStoresAutomaticallyOption
键为[NSNumber
numberWithBool:YES]
时添加
持久存储。
而不是合并
捆绑包中的所有型号,我指定了这两个
我想使用的模型(模型 1 和
新版本的模型 2) 并合并
他们使用modelByMergingModels:
问题
无论我做什么迁移,我都会收到错误消息:
“持久存储迁移失败, 缺少源托管对象模型。”
我的尝试
每次构建后我都会进行清理。 我尝试了各种组合 只有我要迁移的模型 到资源中,正在编译,或 两个都。 由于错误信息 表示找不到源 我的迁移模型,我试过了 拥有模型的每个版本 资源文件夹和存在 编译。 我确定我不是 犯了一个非常基本的错误 切换回原来的 我的数据模型的版本。该应用程序 运行良好。 我已删除映射 型号及新版 模型,清洁,然后重新创建两者。 我已尝试进行不同的更改 在新模型中 - 删除实体 而是。我束手无策。
我不禁认为我在某个我没有看到的地方犯了一个巨大的错误。有什么想法吗?
【问题讨论】:
【参考方案1】:两种可能:
-
您的应用程序中的源模型与磁盘上的实际存储不匹配。
您的映射模型与您的源模型不匹配。
打开Core Data debugging,您应该能够看到Core Data 在进行迁移时正在寻找的哈希值。将这些哈希值与您存储在磁盘上的内容进行比较,看看它们是否匹配。同样,调试应该让您看到映射模型中的哈希值,以帮助您匹配所有内容。
如果只是您的映射模型未对齐,您可以从 Xcode 的设计菜单中告诉它从源代码更新。如果您缺少磁盘上存储文件的实际源模型,则可以查看版本控制系统或尝试使用自动迁移将该文件迁移到您认为是源的模型。
更新 1
更改源模型和目标模型的位置已移至编辑器窗口底部:
【讨论】:
很好的答案,马库斯。谢谢。我会仔细检查哈希值。我很确定这不是选项 1,但我还是会检查一下。 Xcode 必须有充分的理由显示此错误消息。 @Marcus 我们如何在 Xcode 5 中更新源模型? @Marcus 我没听懂你写的那句话:“你可以告诉它从 Xcode 的设计菜单中的源代码更新” - 在哪里?怎么样? 答案已更新。 Xcode 在过去 6 年中发生了变化。 这个答案最终帮助我缩小了我的长期搜索范围,使用了非常有用的迁移调试选项键,可在提供的链接中找到。com.apple.CoreData.MigrationDebug 1
帮助我看到迁移被应用了两次,并允许我以更好的方式调整代码。【参考方案2】:
而不是将所有模型合并到 捆绑包,我已经指定了两个模型 我想使用(模型 1 和新 模型 2 的版本)并将它们合并 使用 modelByMergingModels:
这似乎不对。为什么要合并模型?您想使用 model 2,从 model 1 迁移您的商店。
来自 NSManagedObjectModel 类参考
modelByMergingModels:
创建一个 来自现有数组的模型 模型。
您不需要对您的源模型(model 1)做任何特殊/特定的事情。只要它在您的包中,自动轻量级迁移过程就会发现并使用它.
我建议放弃您在 Xcode 中创建的映射模型,因为与自动轻量级迁移相比,我有 seen terrible performance。您的里程可能会有所不同,我在模型之间的更改与您的不同,但我不会感到惊讶。尝试在捆绑包中使用和不使用您自己的映射模型的时间。
/* Inferred mapping */
NSError *error;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,nil];
NSPersistentStore *migratedStore = [persistentStoreCoordinator addPersistentStoreWithType:nil
configuration:nil
URL:self.storeURL
options:options
error:&error];
migrationWasSuccessful = (migratedStore != nil);
您可以在代码中验证您的源模型是否可用,方法是尝试加载它并验证它不是 nil:
NSString *modelDirectoryPath = [[NSBundle mainBundle] pathForResource:@"YourModelName" ofType:@"momd"];
if (modelDirectoryPath == nil) return nil;
NSString *modelPath = [modelDirectoryPath stringByAppendingPathComponent:@"YourModelName"];
NSURL *modelFileURL = [NSURL fileURLWithPath:modelPath];
NSManagedObjectModel *modelOne = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelFileURL];
if (modelOne == nil)
NSLog(@"Woops, Xcode lost my source model");
else
[modelOne release];
这假设您的项目中有一个资源“YourModelName.xcdatamodeld”和“YourModelName.xcdatamodel”。
此外,您可以检查该模型是否与您现有的迁移前持久存储兼容:
NSError *error;
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:self.storeURL error:&error];
if (storeMeta == nil)
// Unable to read store meta
return NO;
BOOL isCompatible = [modelOne isConfiguration:nil compatibleWithStoreMetadata:storeMeta];
该代码假定您有一个方法 -storeURL
来指定从何处加载持久存储。
【讨论】:
ohhorob:“这似乎不对。为什么要合并模型?您想使用模型 2,从模型 1 迁移您的商店。”我相信他的两个模型适用于两组不同的实体/属性/关系。这两个模型没有相同的实体——它们不相关。类似于员工/部门的模型 1 和产品/客户的模型 2。【参考方案3】:当我收到这个错误时,我已经更新了我的核心数据模型,但没有从我的测试手机中清除应用程序实例。这意味着保存到手机核心数据的模型与我在代码中尝试使用的模型不匹配。
我从手机中删除了应用程序并成功重新构建/运行。
【讨论】:
为我修复了它。谢谢!【参考方案4】:在尝试升级现有应用的核心数据模型(并迁移旧数据)时,我遇到了第三方框架正在将数据写入应用数据库的场景。我收到了这个错误,“找不到源商店的模型。”因为我尝试迁移时没有加载第三方模型,所以迁移失败。
我在解决此问题时编写了此方法(如下)。它可能对面临这些类型问题的人有用。
- (BOOL) checkModelCompatibilityOfStoreWithURL: (NSURL *) myStoreURL
forModelNamed: (NSString *) myModelName
withBasePath: (NSString *) myBasePath;
NSError * error = nil;
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:myStoreURL error:&error];
if (!storeMeta)
NSLog(@"Unable to load store metadata from URL: %@; Error = %@", myStoreURL, error);
return NO;
NSString * modelPath = [myBasePath stringByAppendingPathComponent: myModelName];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: modelPath])
// uh oh
NSLog(@"Can't find model.");
return NO;
NSURL * modelURL = [NSURL fileURLWithPath: modelPath];
NSManagedObjectModel * model = [[[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL] autorelease];
BOOL result = [model isConfiguration: nil compatibleWithStoreMetadata: storeMeta];
NSLog(@"Tested model, %@, is%@ compatible with Database", modelPath, result ? @"" : @" ~not~");
return result;
这段代码 sn-p 将获取商店的元数据。
NSError *error = nil;
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeUrl error:&error];
NSLog(@"%@", [storeMeta objectForKey: @"NSStoreModelVersionHashes"]);
VersionInfo.plist(存储在编译后的应用程序包中)包含与模型中的各种实体相关联的哈希值(base64 编码)。同样,数据存储区 (Z_METADATA.Z_PLIST) 中的 BLOB 列包含一个二进制编码的属性列表,其中包含与数据关联的每个实体的哈希值(也是 base64 编码的)。
NSManagedObjectModel 上的 -entitiesByName 方法对于转储特定模型中存在的实体和哈希很有用。
【讨论】:
嗨 xyzzycoder!我也有类似的问题。你能告诉我在给定的情况下你是如何迁移核心数据的吗(第三方框架正在将数据写入应用程序的数据库)。谢谢 @MohitNigam 这是前一阵子,但我最终直接打开了 sqlite 数据存储(在启动核心数据堆栈之前),删除了第三方的表,更新了元数据条目以删除引用第三方表,关闭 sqlite 文件,然后后台处理 Core Data(然后它能够进行自动迁移)。实际上,我曾就此联系过 WWDC 的 Core Data 团队,但他们没有更好的主意。【参考方案5】:我遇到了类似的问题。我用过+modelByMergeingModels:
,但我没有使用映射模型。但是合并模型不适用于轻量级数据迁移。
来自 Apple 文档:
要执行自动轻量级迁移,Core Data 需要能够找到源和目标托管对象模型自身在运行时。
如果您使用+modelByMergeingModels:
,则它用于目标模型。但是 Core Data 将无法找到源模型。源模型是在旧版本的应用程序中使用+modelByMergeingModels:
创建的,Core Data 确实会尝试合并模型以找出源模型。
我最终做的是,我(手动)通过编辑模型的 XML 文件创建了一个新的合并 .xcdatamodeld
,将其添加到项目中,从编译源中删除了单独的 .xcdatamodeld
s,而不是使用 +modelByMergeingModels:
使用 NSManagedObjectModel
的 -initWithContentsOfURL:
和新合并模型的 URL。我可能会创建一个脚本,将来会自动合并模型。
【讨论】:
【参考方案6】:如果您在 Mac Catalyst 应用程序中遇到此问题,则需要删除可在 ~/Library/Containers/name-of-app 中找到的存储数据
【讨论】:
这为我解决了问题。唯一的问题是容器的名称是散列的(例如7F8E9252-CFFA-446C-A0D7-D00B97DB18DE
),所以我通过查找最近的容器找到它,然后将其删除。以上是关于核心数据迁移问题:“持久存储迁移失败,缺少源托管对象模型。”的主要内容,如果未能解决你的问题,请参考以下文章