我正在尝试对 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)
捆绑包和 url 都指向我期望的位置(并且我已按照 Marcus Zarra 的建议清理项目,因此应用程序包中没有任何杂散的 .mom 或 .momd 捆绑包:使用 mergeModelFromBundles:和版本控制(CoreData))。然而,应用程序在从 url 加载模型时继续崩溃。
我检查了 AccountDocument2.xcdatamodeld 是一个包含两个版本控制模型的包:AccountDocument 2.xcdatamodel 和(原始)AccountDocument.xcdatamodel。文件属性中的“Versioned Core Data Model”弹出菜单设置为“AccountDocument 2”。
两种模型之间的唯一区别是一个实体有一个额外的(和可选的)属性。我的理解是,这使模型有资格进行轻量级迁移。
显然,我在这里做错了什么,但我不知道是什么。任何帮助将不胜感激……</p>
更新:
根据 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;
}
还在崩溃……</p>
更新
在 Twitter 上提出一些建议后,我升级到 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,我建议您在解决此错误之前也这样做。相信我,如果你不这样做,你会花几天时间弄清楚到底发生了什么。
现在提交雷达…</p>
雷达更新
我刚刚提交了这个雷达:
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 之下,您还会发现 beta 工具也无济于事。
希望您有一份带有 Xcode 4.2 的 Snow Leopard 副本:http: //furbo.org/2012/03/28/vmware-for-developers/
WTF 获取请求与版本化实体和属性更新有关
通过 Evadne Wu 在 Twitter 上:
https://twitter.com/#!/evadne/status/187625192342818818
以及她是如何做到的:
https://twitter.com/#!/evadne/status/187629091518816258
(.mom 文件是二进制 plist。)
问题的根源在于单个 Fetch Request。这如何影响数据从一个模型到另一个模型的迁移需要 Apple 的一位工程师来弄清楚。