【发布时间】:2012-04-17 13:34:00
【问题描述】:
我正在尝试对 Core Data 中的 SQLite 存储进行轻量级迁移。使用 Xcode 4.3.1 处理 Lion 10.7.3。
在我的 NSPersistentDocument 子类 (AccountDocument) 中,我重写了用于配置持久存储协调器的方法,以便它获得适当的迁移选项:
- (BOOL)configurePersistentStoreCoordinatorForURL:(NSURL *)url ofType:(NSString *)fileType modelConfiguration:(NSString *)configuration storeOptions:(NSDictionary *)storeOptions error:(NSError **)error
{
NSMutableDictionary *newStoreOptions;
if (storeOptions == nil) {
newStoreOptions = [NSMutableDictionary dictionary];
}
else {
newStoreOptions = [storeOptions mutableCopy];
}
[newStoreOptions setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
[newStoreOptions setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
BOOL result = [super configurePersistentStoreCoordinatorForURL:url ofType:fileType modelConfiguration:configuration storeOptions:newStoreOptions error:error];
return result;
}
(感谢 Malcolm Crawford 的提示:http://homepage.mac.com/mmalc/CocoaExamples/controllers.html)
当我运行应用程序时,它在 NSPersistentDocument 的 -managedObjectModel 实现中失败:
* thread #1: tid = 0x2703, 0x00007fff931d9350 libobjc.A.dylib`objc_msgSend_vtable13 + 16, stop reason = EXC_BAD_ACCESS (code=13, address=0x0)
frame #0: 0x00007fff931d9350 libobjc.A.dylib`objc_msgSend_vtable13 + 16
frame #1: 0x00007fff8935e975 CoreData`-[NSKnownKeysDictionary1 _setValues:retain:] + 197
frame #2: 0x00007fff8935f288 CoreData`_newReadModelFromBytes + 648
frame #3: 0x00007fff8935b93e CoreData`+[NSManagedObjectModel(_NSManagedObjectModelPrivateMethods) _newModelFromOptimizedEncoding:error:] + 9310
frame #4: 0x00007fff89359451 CoreData`-[NSManagedObjectModel(_NSManagedObjectModelPrivateMethods) initWithContentsOfOptimizedURL:] + 305
frame #5: 0x00007fff89358d7b CoreData`-[NSManagedObjectModel initWithContentsOfURL:] + 443
frame #6: 0x00007fff893e9519 CoreData`+[NSManagedObjectModel mergedModelFromBundles:] + 377
frame #7: 0x00007fff8ded7037 AppKit`-[NSPersistentDocument managedObjectModel] + 301
frame #8: 0x00007fff8ded70b3 AppKit`-[NSPersistentDocument managedObjectContext] + 75
frame #9: 0x00007fff8ded6e3f AppKit`-[NSPersistentDocument _persistentStoreCoordinator] + 18
frame #10: 0x00007fff8ded6b5d AppKit`-[NSPersistentDocument configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error:] + 51
frame #11: 0x0000000100003193 BeanCounter`-[AccountDocument configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error:] + 419 at AccountDocument.m:298
从文档中我可以看出,默认实现如下所示:
- (id)managedObjectModel
{
NSManagedObjectModel *result = [NSManagedObjectModel mergedModelFromBundles:nil];
return result;
}
所以为了进一步调试问题,我用这个覆盖了那个方法:
- (id)managedObjectModel
{
NSBundle *bundle = [NSBundle mainBundle];
NSURL *url = [bundle URLForResource:@"AccountDocument2" withExtension:@"momd"];
NSManagedObjectModel *result = [[[NSManagedObjectModel alloc] initWithContentsOfURL:url] autorelease];
return result;
}
(感谢 Jeff LaMarche 的想法:http://iphonedevelopment.blogspot.com/2009/09/core-data-migration-problems.html)
bundle 和 url 都指向我期望的位置(我已按照 Marcus Zarra 的建议清理项目,因此应用程序包中没有任何杂散的 .mom 或 .momd 包:Using mergedModelFromBundles: and versioning (CoreData))。然而,从 url 加载模型时,应用程序继续崩溃。
我检查了 AccountDocument2.xcdatamodeld 是一个包含两个版本控制模型的包:AccountDocument 2.xcdatamodel 和(原始)AccountDocument.xcdatamodel。文件属性中的“Versioned Core Data Model”弹出菜单设置为“AccountDocument 2”。
两个模型之间的唯一区别是一个实体有一个额外的(和可选的)属性。我的理解是,这符合轻量级迁移的模型。
显然,我在这里做错了什么,但我不知道是什么。任何帮助将不胜感激……
更新:
根据 Martin 的建议(以及对 NSPersistentDocument 文档的检查),我尝试将此代码用于访问器:
- (id)managedObjectModel
{
static id sharedManagedObjectModel = nil;
if (sharedManagedObjectModel == nil) {
NSBundle *bundle = [NSBundle mainBundle];
NSURL *url = [bundle URLForResource:@"AccountDocument2" withExtension:@"momd"];
sharedManagedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];
}
return sharedManagedObjectModel;
}
还在崩溃……
更新
在推特上提出一些建议后,我升级到 Xcode 4.3.2,但问题依旧。
愤怒更新
我刚刚在 Snow Leopard 上使用 Xcode 4.2 创建了版本化模型包 (AccountDocument2.xcdatamodeld)。构建并运行应用程序后,一切正常。
然后我将 AccountDocument2.xcdatamodeld 文件包带回 Lion 和 Xcode 4.3.2。当我构建并运行该应用程序时,它在加载 .momd 资源时继续崩溃。是的,孩子们,这意味着 Xcode 4.3.x 和数据模型编译器 (MOMC) 是罪魁祸首。除了在 Snow Leopard 上进行所有构建之外,我没有看到其他解决方法。
我不是抨击 Xcode 4 的人,但是当我们发现自己处于工具链无法从不透明规范(.xcdatamodel 和 .xcdatamodeld)生成不透明文件(.mom 和 .momd)的情况下很难对 Mac 和 iOS 工具的状态感到乐观。荒谬的是,这些平台的核心组件损坏到我无法在最新版本的 SDK 和开发人员工具上构建和运行我的应用程序的程度。
本次更新来了
更多证据表明这是 Xcode 4.3.2 中数据模型编译器 (MOMC) 的一个严重错误:如果我将 Xcode 4.2 创建的 Resource 文件夹中的 .momd 包复制到我的项目中并将它们添加到构建中复制文件构建阶段,应用程序运行良好。
我还做了一些测试,删除了各种实体的属性的验证规则和默认值(基于下面 Marcus 的建议。)没有变化,编译器仍然会创建一个无效的 .momd。我还尝试创建一个没有任何更改的版本化模型:编译后的 .momd 继续崩溃。因此,您当前模型中的任何内容(以及它们所代表的数据)都是问题的根源。
另外值得注意的是:这个错误并不是孤立于 NSPersistentDocument 的(正如我最初提出这个问题时所想的那样。)我可以通过使用 [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] 来导致应用程序崩溃。
现在,我将在 Snow Leopard 上使用 Xcode 4.2 编辑/版本控制我的模型,并将编译后的资源转移到 Lion 上的 Xcode 4.3.2。如果您以任何方式使用 Core Data,我建议您在解决此错误之前也这样做。相信我,如果你不这样做,你会花几天时间弄清楚到底发生了什么。
现在提交雷达…
雷达更新
我刚刚提交了这个雷达:
http://www.openradar.me/11184500
哦,废话,一定是狮子更新
我刚刚从http://developer.apple.com/downloads 下载并安装了 Xcode 4.2 for Lion 工具。 Radar 中使用的示例应用程序仍然崩溃。
(注意:您无法安装 Xcode 4.2.1,因为用于签署 DeveloperTools.pkg 的证书已过期。只有 Xcode 4.2 可以使用。)
如果您处于 NDA 之下,您还会发现测试版工具也没有帮助。
希望你有一份带有 Xcode 4.2 的 Snow Leopard:http://furbo.org/2012/03/28/vmware-for-developers/
WTF 获取请求是否与版本化实体和属性更新有关
通过 Twitter 上的 Evadne Wu:
https://twitter.com/#!/evadne/status/187625192342818818
她是怎么做到的:
https://twitter.com/#!/evadne/status/187629091518816258
(.mom 文件是二进制 plist。)
问题的根源在于单个 Fetch Request。 Apple 的工程师需要弄清楚如何将数据从一个模型迁移到另一个模型。
【问题讨论】:
-
您是否在定义
configurePersistentStoreCoordinatorForURL:::::的类中使用ARC?如果没有,您将泄露您制作的storeOptions副本。 -
根据现有的 storeOptions 是否存在,newStoreOptions 将被保留(如果你调用 [storeOptions mutableCopy])或者如果你遵循调用 [NSMutableDictionary dictionary] 的路径则自动释放
-
Peter 说得对,newStoreOptions 泄漏是对的,但这并不重要,因为我刚刚检查了代码路径并且 storeOptions 以 nil 的形式出现。这不是加载模型时问题的根源(模型无法加载,更不用说迁移了。)
-
@chockenberry 您可能希望提交上一两次更新作为答案,而不是作为对原始帖子的编辑。
-
我认为写一个带有完整解释的实际答案而不是简单地链接到这些推文会很有用。然后,为将来提出此问题的人标记答案。
标签: objective-c cocoa core-data osx-lion nspersistentdocument