objective-c - NSPersistentDocument 的轻量级迁移

标签 objective-c cocoa core-data osx-lion nspersistentdocument

我正在尝试对 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;
}

还在崩溃……

更新

在 Twitter 上提出一些建议后,我升级到 Xcode 4.3.2,但问题仍然存在。

愤怒更新

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

然后我将 AccountDocument2.xcdatamodeld 文件包带回 Lion 和 Xcode 4.3.2。当我构建并运行应用程序时,它在加载 .momd 资源时继续崩溃。是的, children ,这意味着 Xcode 4.3.x 和数据模型编译器 (MOMC) 是罪魁祸首。除了在 Snow Leopard 上进行所有构建之外,我看不到其他解决方法。

我不是一个抨击 Xcode 4 的人,但是当我们发现自己处于工具链无法根据不透明规范(.xcdatamodel 和 .xcdatamodeld)生成不透明文件(.mom 和 .momd)的情况时,很难对 Mac 和 iOS 工具的状态保持乐观。荒谬的是,这些平台的核心组件出现了问题,以至于我无法在最新版本的 SDK 和开发人员工具上构建和运行我的应用程序。

来此更新

更多证据表明这是 Xcode 4.3.2 中数据模型编译器 (MOMC) 的严重错误:如果我将 .momd 包从 Xcode 4.2 创建的资源文件夹复制到我的项目中,并将它们作为复制文件添加到构建中构建阶段,应用程序工作正常。

我还做了一些测试,删除了各种实体的属性的验证规则和默认值(基于下面 Marcus 的建议)。没有变化,编译器仍然创建了一个无效的 .momd。我还尝试创建一个没有任何更改的版本化模型:编译后的 .momd 继续崩溃。因此,无论您在当前模型(以及它们代表的数据)中拥有什么,都是问题的根源。

另请注意:此错误并非孤立于 NSPersistentDocument(正如我最初提出此问题时所想的那样。)我可以通过使用 [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] 导致应用程序崩溃。 .

现在,我将在 Snow Leopard 上使用 Xcode 4.2 编辑/版本控制我的模型,并将编译后的资源转移到 Lion 上的 Xcode 4.3.2。如果你以任何方式使用 Core Data,我建议你也这样做,直到这个 bug 得到解决。相信我,如果你不这样做,你会花几天的时间试图弄清楚到底发生了什么。

现在提交雷达…

雷达更新

我刚刚提交了这个雷达:

http://www.openradar.me/11184500

哦废话一定是狮子更新

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

(注意:您无法安装 Xcode 4.2.1,因为用于签署 DeveloperTools.pkg 的证书已过期。只有 Xcode 4.2 可以工作。)

如果您处于保密协议(protocol)之下,您还会发现测试版工具也无济于事。

希望你有一份使用 Xcode 4.2 的 Snow Leopard 的副本:http://furbo.org/2012/03/28/vmware-for-developers/

WTF 确实获取请求与版本化实体和属性更新有关

通过 Evadne Wu 在推特上:

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

以及她是如何做到的:

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

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

问题的根源在于单个 Fetch 请求。如何将数据从一种模型迁移到另一种模型需要 Apple 的工程师弄清楚。

最佳答案

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

name == $name

到:
name == $NAME

不影响数据持久性的对象模型的一部分破坏了版本控制和轻量级迁移,这是违反直觉的。从文档中可以清楚地看出,情况不应该是这样:

Core Data’s perspective on versioning is that it is only interested in features of the model that affect persistence.



使用 CHOCKLOCK 的力量修复您的提取请求或完全删除它们并依赖 NSPredicates created in code .

关于objective-c - NSPersistentDocument 的轻量级迁移,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10001026/

相关文章:

cocoa - 如何限制 NSPopUpButton 的 "pop-up"大小?

objective-c - 多个 UIActionSheets 一次打开

iphone - 试图获得接触点,返回 NaN

php - PHP 开发人员的 Cocoa 技巧?

iPhone - 数据库查询返回未提交的条目?

iphone - 将核心数据与 JSON API 同步的最有效方法是什么

ios - 如何获取具有关系的核心数据对象实例?

objective-c - 问题过滤 NSArrayController

iOS 11 强制 UIViewController 到纵向模式不起作用

objective-c - cocoa 。如何仅在托盘应用程序中显示窗口