轻量级迁移NSPersistentDocument

我正在尝试在Core Data中进行SQLite存储的轻量级迁移。 在Xcode 4.3.1上使用Lion 10.7.3。

在我的NSPersistentDocument子类(AccountDocument)中,我重写了用于configuration持久性存储协调器的方法,以便获得适当的迁移选项:

- (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; } 

所以要debugging一下这个问题,我用这个方法覆盖了这个方法:

 - (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的build议来清理项目,所以在应用程序包中没有任何杂散的.mom或.momd包: 使用mergedModelFromBundles:和版本控制(CoreData) )。 然而,从url加载模型时,应用程序仍然崩溃。

我已经检查了AccountDocument2.xcdatamodeld是一个包含两个版本模型的包:AccountDocument 2.xcdatamodel和(原始)AccountDocument.xcdatamodel。 文件属性中的“Versioned Core Data Model”popup菜单设置为“AccountDocument 2”。

两个模型之间唯一的区别是一个实体具有额外的(和可选的)属性。 我的理解是,对于轻量级迁移的模型是合格的。

显然,我在这里做错了什么,但我不知道是什么。 非常感激任何的帮助…

更新:

每马丁的build议(和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; } 

仍然崩溃…

更新

在Twitter上的一些build议后,我升级到Xcode 4.3.2,但问题依然存在。

愤怒更新

我刚刚在Snow Leopard上使用Xcode 4.2创build了版本化的模型包(AccountDocument2.xcdatamodeld)。 build立和运行应用程序后,一切都按预期工作。

然后,我将AccountDocument2.xcdatamodeld文件包返回到Lion和Xcode 4.3.2。 当我构build并运行应用程序时,它在加载.momd资源时会继续崩溃。 是的,孩子们,这意味着Xcode 4.3.x和数据模型编译器(MOMC)是责怪。 除了在Snow Leopard上做所有的构build之外,我没有看到一个解决方法。

我不是一个打破Xcode 4,但是当我们发现自己的工具链不能从一个不透明的规范(.xcdatamodel和.xcdatamodeld)产生一个不透明的文件(.mom和.momd)的情况下,很难对Mac和iOS工具的状态感到乐观。 这些平台的核心组件突破了我无法在最新版本的SDK和开发工具上构build和运行我的应用的程度,这是非常荒谬的。

这是更新

更多certificate这是Xcode 4.3.2中的数据模型编译器(MOMC)的一个严重的错误:如果我将.momd包从Xcode 4.2创build的Resource文件夹复制到我的项目中,并将它们作为复制文件build立阶段,应用程序正常工作。

我也做了一些testing,我删除了各种实体的属性的validation规则和默认值(基于下面的Marcus的build议)。没有变化,编译器仍然创build一个无效的.momd。 我也尝试创build一个没有任何改变的版本模型:编译后的.momd继续崩溃。 所以,无论你现在的模型(和他们所代表的数据)是什么,都是问题的根源。

另外值得注意的是:这个bug并不是孤立于NSPersistentDocument(正如我最初想到的,当我开始这个问题时)。我可以通过使用[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL]导致应用程序崩溃。

现在,我将在Snow Leopard上使用Xcode 4.2编辑/版本化我的模型,并将编译后的资源移到Lion上的Xcode 4.3.2上。 如果你以任何方式使用核心数据,我build议你也一样,直到解决这个问题。 相信我,如果不这样做的话,你会花几天的时间去弄清楚到底发生了什么。

现在提交一个雷达…

雷达更新

我刚刚提交了这个雷达:

http://www.openradar.me/11184500

哦,废话它必须是狮子更新

我刚刚从http://developer.apple.com/downloads下载并安装了Lion工具的Xcode 4.2。 雷达中使用的示例应用程序仍然崩溃。

(注意:你不能安装Xcode 4.2.1,因为用于签署DeveloperTools.pkg的证书已经过期,只有Xcode 4.2能工作。)

如果你使用NDA,你也会发现testing工具也没有帮助。

希望你有一个Xcode 4.2的雪豹副本: http : //furbo.org/2012/03/28/vmware-for-developers/

WTF Do提取请求必须使用版本化的实体和属性更新

通过Evadne Wu Twitter:

https://twitter.com/#!/evadne/status/187625192342818818

而她是如何做到的:

https://twitter.com/#!/evadne/status/187629091518816258

(.mom文件是二进制plists。)

问题的根源是单个提取请求。 如何将数据从一个模型迁移到另一个模型是由苹果的一名工程师弄清楚的。

已编译的.momd资源可以在“existingPartner”提取请求从以下位置更改之后加载:

 name == $name 

至:

 name == $NAME 

不影响数据持久性的部分对象模型会违反版本控制和轻量级迁移,这是违反直觉的。 从文档中可以明显看出,情况并非如此:

核心数据对版本的看法是它只对影响持久性的模型的特性感兴趣。

使用CHOCKLOCK的强大function修复您的提取请求或完全删除它们,并依靠代码中创build的NSPredicates 。

我想你需要将托pipe对象模型存储在一个实例variables中。 您正在返回一个autoreleased对象,这可能是导致访问不良的原因。

根据你的理论,这是MOMC的问题,你有没有任何validation规则在妈妈?

我已经看到了validation规则在4.x MOMC中无法生存的报告。

这可能与我在使用提取请求时遇到的一个问题有些相关,当iOS5首次推出testing版时。 这是造成build立警告,并会在启动时崩溃的应用程序。 我没有真正使用抓取请求,所以我删除它,一切工作正常: 核心数据警告:“版本哈希信息不适用于所有型号”