代码之家  ›  专栏  ›  技术社区  ›  John Gallagher

核心数据迁移问题:“持久存储迁移失败,缺少源托管对象模型。”

  •  8
  • John Gallagher  · 技术社区  · 15 年前

    背景

    • 具有两个托管对象的项目
    • 型号1保持不变。模型 2已更改,所以我要迁移
    • 我已经创建了一个新版本 按设计>数据模型>添加模型
    • 版本之间的差异是一个单一的关系,从一个变为多个。
    • 我已经做好了我的工作 更改模型,然后保存。
    • 我做了一个新的映射模型 以旧模型为源,以新模型为源 作为目的地的模型。
    • 所有映射模型和数据模型 已复制到我的资源文件夹 应用程序包。
    • 我已经启动了迁移 把字典交给 NSMigratePersistentStoresAutomaticallyOption 钥匙组件 [NSNumber numberWithBool:YES] 添加 持久存储。
    • 而不是合并 捆绑包中的所有型号,我都指定了两种 我想使用的模型(模型1和 新版本的模型2)和合并 modelByMergingModels:

    问题

    无论我如何迁移,都会收到错误消息:

    “持久存储迁移失败,

    • 我每次建造后都会打扫。
    • 只有我要移植的模型 在资源中,正在编译,或
    • 自从错误消息 意味着它找不到源头 把模型的每一个版本都放在 资源文件夹和
    • 犯了一个非常基本的错误 我的数据模型的版本。应用程序
    • 我已经删除了映射 模型和新版本的
    • 我试着做出不同的改变 在新模型中-删除实体

    我忍不住想我在某个我看不到的地方犯了一个巨大的错误。有什么想法吗?

    5 回复  |  直到 14 年前
        1
  •  15
  •   Marcus S. Zarra    7 年前

    两种可能性:

    1. 应用程序中的源模型与磁盘上的实际存储不匹配。

    打开 Core Data debugging 您应该能够看到核心数据在进行迁移时所寻找的哈希值。将这些哈希值与存储在磁盘上的内容进行比较,看看它们是否匹配。同样,调试应该让您看到映射模型中的散列,以帮助您匹配所有内容。

    更新1

    更改源模型和目标模型的位置已移动到编辑器窗口的底部:

        2
  •  6
  •   idmean    6 年前

    而不是合并 bundle,我已经指定了两种型号 我想使用(模型1和新的 使用modelByMergingModels:

    这似乎不对。为什么要合并模型?你想用 模型1

    从NSManagedObjectModel类参考

    modelByMergingModels:

    创建单个 模型。

    模型1 ).. 只要它在您的包中,自动轻量级迁移过程就会发现并使用它。

    我建议放弃您在Xcode中创建的映射模型,正如我之前所做的那样 seen terrible performance 与自动轻量级迁移相比。您的里程数可能会有所不同,我在不同车型之间的变化与您的不同,但我不会感到惊讶。尝试在捆绑包中使用和不使用您自己的映射模型的情况下进行计时。

     /* Inferred mapping */
     NSError *error;
     NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                              [NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
                              [NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption,nil];
     NSPersistentStore *migratedStore = [persistentStoreCoordinator addPersistentStoreWithType:nil
                                                                                 configuration:nil
                                                                                           URL:self.storeURL
                                                                                       options:options
                                                                                         error:&error];
     migrationWasSuccessful = (migratedStore != nil);
    

    NSString *modelDirectoryPath = [[NSBundle mainBundle] pathForResource:@"YourModelName" ofType:@"momd"];
    if (modelDirectoryPath == nil) return nil;
    NSString *modelPath = [modelDirectoryPath stringByAppendingPathComponent:@"YourModelName"];
    NSURL *modelFileURL = [NSURL fileURLWithPath:modelPath];
    NSManagedObjectModel *modelOne = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelFileURL];
    if (modelOne == nil) {
        NSLog(@"Woops, Xcode lost my source model");
    }
    else {
        [modelOne release];
    }
    

    YourModelName.xcdatamodeld “和” YourModelName.xcdatamodel “在它里面。


    此外,您还可以检查该模型是否与现有的迁移前持久存储兼容:

    NSError *error;
    NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:self.storeURL error:&error];
    if (storeMeta == nil) {
        // Unable to read store meta
        return NO;
    }
    BOOL isCompatible = [modelOne isConfiguration:nil compatibleWithStoreMetadata:storeMeta];
    

    该代码假定您有一个方法 -storeURL 指定从何处加载永久存储。

        3
  •  4
  •   a_lovelace    6 年前

    当我得到这个错误,我已经更新了我的核心数据模型,但没有清除我的测试手机的应用程序实例。这意味着保存到手机核心数据的模型与我试图在代码中使用的不匹配。

    我从手机中删除了该应用程序,并成功地重新构建/运行。

        4
  •  3
  •   xyzzycoder    14 年前

    在尝试升级现有应用程序的核心数据模型(并迁移遗留数据)时,我遇到了一个场景,第三方框架正在将数据写入应用程序的数据库。我遇到了这个错误,“找不到源存储的模型”。因为在我尝试迁移时没有加载第三方模型,所以迁移失败。

    我在解决这个问题的过程中编写了这个方法(如下)。它可能对那些面临这类问题的人有用。

    - (BOOL) checkModelCompatibilityOfStoreWithURL: (NSURL *) myStoreURL
                                     forModelNamed: (NSString *) myModelName
                                      withBasePath: (NSString *) myBasePath;
    {
        NSError * error = nil;
        NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:myStoreURL error:&error];
        if (!storeMeta) {
            NSLog(@"Unable to load store metadata from URL: %@; Error = %@", myStoreURL, error);
            return NO;
        }
    
        NSString * modelPath = [myBasePath stringByAppendingPathComponent: myModelName];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if (![fileManager fileExistsAtPath: modelPath]) {
            // uh oh
            NSLog(@"Can't find model.");
            return NO;
        }
    
        NSURL * modelURL = [NSURL fileURLWithPath: modelPath];
        NSManagedObjectModel * model = [[[NSManagedObjectModel alloc] initWithContentsOfURL: modelURL] autorelease];
        BOOL result = [model isConfiguration: nil compatibleWithStoreMetadata: storeMeta];
    
        NSLog(@"Tested model, %@, is%@ compatible with Database", modelPath, result ? @"" : @" ~not~");
    
        return result;
    }
    

    此代码段将获取存储的元数据。

    NSError *error = nil;
    NSURL *storeUrl = [NSURL fileURLWithPath:storePath];
    NSDictionary *storeMeta = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:nil URL:storeUrl error:&error];
    NSLog(@"%@", [storeMeta objectForKey: @"NSStoreModelVersionHashes"]);
    

    VersionInfo.plist(存储在已编译的应用程序包中)包含与模型中的各种实体(base64编码)关联的哈希。类似地,数据存储(Z_METADATA.Z_PLIST)中的BLOB列包含二进制编码的属性列表,该属性列表具有与数据关联的每个实体的哈希值(也是base64编码的)。

    NSManagedObjectModel上的-entitiesByName方法对于转储特定模型中存在的实体和散列非常有用。

        5
  •  1
  •   Marián Černý    10 年前

    我也有类似的问题。我用过 +modelByMergeingModels:

    来自苹果文档:

    要执行自动轻量级迁移,核心数据需要能够 和目标托管对象 模型 运行时 .

    如果你使用 +modelByMergeingModels模型: 而不是用于目标模型。然而,核心数据将无法找到源模型。已使用创建源模型 +modelByMergeingModels模型: 在旧版本的应用程序和核心数据中,确实尝试合并模型以找出源模型。

    我最终做的是(手动)创建了一个新的合并 .xcdatamodeld 通过编辑模型的XML文件,将其添加到项目中,删除单独的 .xcdatamodeld +modelByMergeingModels模型: NSManagedObjectModel -initWithContentsOfURL: 新合并模型的URL。我可能会创建一个脚本,在将来自动合并模型。