+[NSManagedObjectModel mergeModelFromBundles::forStoreMetadata:] 总是返回 nil
Posted
技术标签:
【中文标题】+[NSManagedObjectModel mergeModelFromBundles::forStoreMetadata:] 总是返回 nil【英文标题】:+[NSManagedObjectModel mergedModelFromBundles::forStoreMetadata:] always returns nil 【发布时间】:2019-04-06 02:44:10 【问题描述】:我有一个包含 15 个版本的 Core Data 模型。它的代码可以在发布时从当前商店的版本逐步迁移到最新版本。
关键是调用
NSDictionary* options = @ NSMigratePersistentStoresAutomaticallyOption : @true,
NSInferMappingModelAutomaticallyOption : @true ;
NSDictionary* sourceMetadata = [NSPersistentStoreCoordinator
metadataForPersistentStoreOfType: inType
URL: inSourceStore
options: options
error: outError];
NSManagedObjectModel* model = [NSManagedObjectModel mergedModelFromBundles: @[ [NSBundle bundleForClass: [self class]] ]
forStoreMetadata: inSourceMetadata];
但这总是返回 nil,我不知道为什么。现有商店是14版,新型号是15版。
现在,对模型的最后一次更改相当简单(添加了几个可选字段),所以我原以为它可以自动推断映射,但那不起作用,所以我添加了一个映射模型版本 14 到版本 15 使用 Xcode 的助手,并且没有做任何更改。
知道为什么它返回 nil,或者我可以做些什么来进一步调查?
同样,当我说“版本 14”时,我指的是.xcdatamodel
文件的顺序编号。有什么方法可以查看实际存储并确定 Core Data 认为它是哪个版本的模型?
【问题讨论】:
【参考方案1】:嗯,首先,您似乎知道自己在做什么,经历了 14 次核心数据迁移等等。所以我认为你应该注意一些愚蠢的打额头的错误。
确保[NSBundle bundleForClass: [self class]]
正在返回预期的捆绑包,其中包含一个目录Contents/Resources/YourModelName.momd
,并且该目录包含所有必需的.mom
文件(每个版本一个)和一个VersionInfo.plist
文件。我的构建还包含一个 .omo
文件,仅供最新版本使用。
现在我来回答你的第二个问题,这确实可以帮助你回答你的第一个问题。
在该VersionInfo.plist
文件中,您将找到一个名为NSManagedObjectModel_VersionHashes
的字典,该字典又包含子字典,每个版本一个键。每个版本子字典都包含每个实体名称和值的键,它是该版本中该实体的属性和关系的 32 字节(256 位)散列。我们称其为 模型哈希。
现在使用 SQLite 查看器或sqlite3
命令行工具打开一个商店数据库文件。在该数据库中,除了模型中每个实体的一个表之外,您将看到一个名为 Z_METADATA
的表,其中包含一行和三列。名为Z_PLIST
的列的值被键入为二进制数据块。将该数据复制到一个文件中,使用扩展名.plist
命名,双击它,它会在您最喜欢的 plist 编辑器中打开,因为该数据实际上是表示 XML 格式的 Apple 属性列表的文本字符串。它的键NSStoreModelVersionHashes
的值实际上是一个子字典,就像VersionInfo.plist
文件中的子字典一样。我们称之为存储哈希。 32 字节(256 位)版本哈希是 Base64 编码的。 (有44个Base64字符。由于每个Base64字符代表6位,44个字符最多可以代表44*6 = 264位。)
最后,回答您的第二个问题,传递给+[NSManagedObjectModel mergedModelFromBundles:forStoreMetadata:]
的storeMetadata
实际上是来自商店的Z_METADATA
,其中包含那些商店哈希。 +[NSManagedObjectModel mergedModelFromBundles:forStoreMetadata:]
将这些 store hashes 与传入 bundle
中每个候选数据模型的 model hashes 进行比较,并返回其 model hashes 匹配所有实体的存储哈希,两边都没有额外的不匹配实体。
因此,您会发现手动进行比较有点乏味。但可能在探索这些 plists 时,你会发现额头拍打。如果没有,请给我们一些关于您粘贴的代码的更多上下文,可能有人可以提供帮助。
【讨论】:
当我发布我的答案时,您可能一直在撰写该答案。很抱歉浪费您的时间!无论如何,我想通了(你可以看到它一直是用户错误)。不过,您提供了一些关于 SQLite DB 的有用信息,谢谢! 没问题,瑞克。这不是浪费。有时会发生这种情况。【参考方案2】:啊。事实证明,我编辑了最新的模型版本,而不是添加一个新版本。这就是为什么没有人会匹配。一旦我恢复了最新版本并添加了带有更改的新模型版本,即使没有默认映射模型,它也能正常工作。
我通过测试每个模型以查看它是否与源元数据匹配来解决这个问题,使用以下方法:
NSDictionary* storeHashes = [sourceMetadata objectForKey: NSStoreModelVersionHashesKey];
NSArray<NSURL*>* urls = [self getModelURLs];
urls = [urls sortedArrayUsingComparator:
^NSComparisonResult(id _Nonnull obj1, id _Nonnull obj2)
NSURL* s2 = obj1;
NSURL* s1 = obj2;
return [s1.lastPathComponent compare: s2.lastPathComponent options: NSNumericSearch];
];
for (NSURL* url in urls)
NSDictionary* modelHashes = [self getHashesForModelAtURL: url];
// Compare the hashes…
bool matches = true;
for (NSString* entityKey in storeHashes.allKeys)
NSString* storeHash = storeHashes[entityKey];
NSString* modelHash = modelHashes[entityKey];
if (![storeHash isEqual: modelHash])
NSLogDebug(@"Model %@ has mismatch on %@", url.lastPathComponent, entityKey);
matches = false;
if (matches)
NSLogDebug(@"Version matches: %@", url.lastPathComponent);
break;
- (NSArray<NSURL*>*)
getModelURLs
NSBundle* bundle = [NSBundle bundleForClass: [self class]];
NSArray<NSURL*>* urls = [bundle URLsForResourcesWithExtension: @"mom" subdirectory: @"Model.momd"];
return urls;
- (NSDictionary*)
getHashesForModelAtURL: (NSURL*) inURL
NSManagedObjectModel* model = [[NSManagedObjectModel alloc] initWithContentsOfURL: inURL];
NSDictionary* hashes = model.entityVersionHashesByName;
return hashes;
【讨论】:
以上是关于+[NSManagedObjectModel mergeModelFromBundles::forStoreMetadata:] 总是返回 nil的主要内容,如果未能解决你的问题,请参考以下文章
NSManagedObject、NSManagedObjectContext 和 NSManagedObjectModel 的区别
NSManagedObjectModel 合并模型FromBundles 错误
尝试使用前向类“NSManagedObjectModel”作为 Swift 类模型的超类
NSInternalConsistencyException 原因 +entityForName:找不到实体名称的 NSManagedObjectModel
+entityForName: 找不到实体名称的 NSManagedObjectModel
NSManagedObjectModel initWithContentsOfURL 返回 nil 即使 modelURL 是有效的