核心数据无法找到源存储的模型 - 我的旧商店看起来像什么?

时间:2010-08-27 15:41:57

标签: iphone core-data versioning

首先,this question为实现核心数据版本控制的正确轨道提供了很多帮助。所以我为我的模型添加了一个新版本,现在我正在尝试让自动迁移工作,但我遇到了问题。我不记得我的旧版本是什么样的!我正在尝试在手机上运行应用程序,但我一直在使用模拟器并对架构进行了一些更改。手机上的版本是很久以前的。所以每次我尝试将旧版本修改为我认为在手机上的内容,但我仍然得到“找不到源代码存储模型”的错误。我猜这是因为我的旧架构错了。

我有什么方法可以弄清楚手机上的架构是什么样的?除此之外,我怎么能从手机上擦除sqlite商店以便从版本1重新开始呢?

4 个答案:

答案 0 :(得分:13)

我整天都在“无法找到源存储模型”错误上撕掉我的头发。以下是针对googlers的learner2010答案的详细说明:

  • 在构建应用程序时,您的sqlite数据库的模型哈希必须与您的xcdatamodel创建的mom或momd之一匹配。您可以在构建的应用程序包中看到momd的VersionInfo.plist中的哈希值。请参阅下面的代码以查找数据库的模型哈希值。

  • 因此,如果您更改xcdatamodel而不是在Xcode-> Editor-> Add Model Version ...下创建新版本,那么您的模型的哈希将不同,并且addPersistentStoreWithType将无法使用旧数据库,使用旧模型。这就是导致“无法找到源存储模型”错误的原因。

  • 更糟糕的是,sqlite数据库存储在“/ private / var / mobile / Library / Mobile Documents / YOU_APP_ID / Data.nosync / YOUR_DB.sqlite”之类的内容中,即使你存在从设备中删除应用程序并重新安装!所以你会认为你的代码有问题,而实际上你只需要一个需要删除的陈旧数据库。通常这是在调试期间,所以无论如何都没有真正的数据。

  • 因此,允许将来迁移的正确工作流程是制作模型,运行应用程序以构建数据库,然后在需要进行更改时随时创建模型的新版本。如果你保持变化很小,一切都会“正常工作”。然后,当您准备好发布应用程序时,请选择最终模型并删除其余模型。然后从“/ private / var / mobile / Library / Mobile Documents”中删除您的数据库。然后在将来的版本中,包括以前版本中的所有模型以及您的最新模型(如果它已更改),用户将能够每次迁移。

到目前为止,这是我的代码。重要的一点是:

[fileManager removeItemAtPath:iCloudData error:&error];

但它只能在调试期间用于删除旧数据库。这是AppDelegate.m中的生产代码:

- (NSManagedObjectModel *)managedObjectModel
{
    if (__managedObjectModel != nil)
    {
        return __managedObjectModel;
    }
    //NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"Model" withExtension:@"momd"];
    //__managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    //NSArray *testArray = [[NSBundle mainBundle] URLsForResourcesWithExtension:@"momd"subdirectory:nil];

    NSString *path = [[NSBundle mainBundle] pathForResource:@"Model" ofType:@"momd"];

    if( !path ) path = [[NSBundle mainBundle] pathForResource:@"Model" ofType:@"mom"];

    NSURL *modelURL = [NSURL fileURLWithPath:path];

    __managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];

    //__managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];

    return __managedObjectModel;
}

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
    if((__persistentStoreCoordinator != nil)) {
        return __persistentStoreCoordinator;
    }

    __persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];    
    NSPersistentStoreCoordinator *psc = __persistentStoreCoordinator;

    // Set up iCloud in another thread:

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

        // ** Note: if you adapt this code for your own use, you MUST change this variable:
        NSString *iCloudEnabledAppID = @"RW6RS7HS69.com.zsculpt.soaktest";

        // ** Note: if you adapt this code for your own use, you should change this variable:        
        NSString *dataFileName = @"mydailysoak.sqlite";

        // ** Note: For basic usage you shouldn't need to change anything else

        NSString *iCloudDataDirectoryName = @"Data.nosync";
        NSString *iCloudLogsDirectoryName = @"Logs";
        NSFileManager *fileManager = [NSFileManager defaultManager];        
        NSURL *localStore = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:dataFileName];
        NSURL *iCloud = [fileManager URLForUbiquityContainerIdentifier:nil];

        if (iCloud) {

            NSLog(@"iCloud is working");

            NSURL *iCloudLogsPath = [NSURL fileURLWithPath:[[iCloud path] stringByAppendingPathComponent:iCloudLogsDirectoryName]];

            NSLog(@"iCloudEnabledAppID = %@",iCloudEnabledAppID);
            NSLog(@"dataFileName = %@", dataFileName); 
            NSLog(@"iCloudDataDirectoryName = %@", iCloudDataDirectoryName);
            NSLog(@"iCloudLogsDirectoryName = %@", iCloudLogsDirectoryName);  
            NSLog(@"iCloud = %@", iCloud);
            NSLog(@"iCloudLogsPath = %@", iCloudLogsPath);

            if([fileManager fileExistsAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName]] == NO) {
                NSError *fileSystemError;
                [fileManager createDirectoryAtPath:[[iCloud path] stringByAppendingPathComponent:iCloudDataDirectoryName] 
                    withIntermediateDirectories:YES 
                                     attributes:nil 
                                          error:&fileSystemError];
                if(fileSystemError != nil) {
                    NSLog(@"Error creating database directory %@", fileSystemError);
                }
            }

            NSString *iCloudData = [[[iCloud path] 
                                     stringByAppendingPathComponent:iCloudDataDirectoryName] 
                                    stringByAppendingPathComponent:dataFileName];

            NSLog(@"iCloudData = %@", iCloudData);

            NSMutableDictionary *options = [NSMutableDictionary dictionary];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];
            [options setObject:iCloudEnabledAppID            forKey:NSPersistentStoreUbiquitousContentNameKey];
            [options setObject:iCloudLogsPath                forKey:NSPersistentStoreUbiquitousContentURLKey];

            [psc lock];
        NSError *error;

            [psc addPersistentStoreWithType:NSSQLiteStoreType 
                              configuration:nil 
                                        URL:[NSURL fileURLWithPath:iCloudData] 
                                    options:options 
                                      error:&error];

            if( error )
            {
                NSLog(@"Error adding persistent store %@, %@", error, [error userInfo]);

                // comment in this line while debugging if get "Can't find model for source store" error in addPersistentStoreWithType.
                // it means the sqlite database doesn't match the new model and needs to be created from scratch.
                // this happens if you change the xcdatamodel instead of creating a new one under Xcode->Editor->Add Model Version...
                // CoreData can only automatically migrate if there is a new model version (it can't migrate if the model simply changes, because it can't see the difference between the two models).
                // be sure to back up the database if needed, because all data will be lost.
                //[fileManager removeItemAtPath:iCloudData error:&error];

                /*// this is another way to verify the hashes for the database's model to make sure they match one of the entries in the momd directory's VersionInfo.plist
                NSDictionary *sourceMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:[NSURL fileURLWithPath:iCloudData] error:&error];

                if( !sourceMetadata )
                    NSLog(@"sourceMetadata is nil");
                else
                    NSLog(@"sourceMetadata is %@", sourceMetadata);*/
            }

            [psc unlock];
        }
        else {
            NSLog(@"iCloud is NOT working - using a local store");
            NSMutableDictionary *options = [NSMutableDictionary dictionary];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSMigratePersistentStoresAutomaticallyOption];
            [options setObject:[NSNumber numberWithBool:YES] forKey:NSInferMappingModelAutomaticallyOption];

            [psc lock];
        NSError *error;

            [psc addPersistentStoreWithType:NSSQLiteStoreType 
                              configuration:nil 
                                        URL:localStore 
                                    options:options 
                                      error:nil];

        if( error )
            NSLog(@"Error adding persistent store %@, %@", error, [error userInfo]);    
            [psc unlock];
        }

        dispatch_async(dispatch_get_main_queue(), ^{
            [[NSNotificationCenter defaultCenter] postNotificationName:@"SomethingChanged" object:self userInfo:nil];
        });
    });

    return __persistentStoreCoordinator;   
}

答案 1 :(得分:5)

错误消息表示无法找到现有商店的.mom已编译模型文件。 Core Data正在寻找配置商店的精确.mom版本。 .mom文件告诉Core Data如何将文件中的序列化数据映射到对象中。如果没有该模型文件,它就不知道如何将商店迁移到新模型,因为它不知道每个实体或实体属性的数据。

我只看过一次,而IIRC的原因是新.mom文件的名称和位置与旧文件完全相同。应用程序更新后,旧的.mom文件被覆盖。

尝试更改新模型文件的名称,看看是否有帮助。如果没有,我们可能需要更多有关您正在做的事情的细节。

答案 2 :(得分:0)

我也有同样的问题。我通过删除旧数据库并再次构建项目来解决它。我不确定这是正确的做法。显然它指向错误的数据库文件。就我而言,我正在使用核心数据创建一个简单的项目,并尝试迁移。当我创建一个带有附加列的新模型并构建我​​的项目时,有2个文件 - coreData.sqlite和coreData~ .sqlite ...所以我觉得,错误的数据库被指向并因此错误。当我删除数据库并再次构建项目时,它对我来说非常有用。

如果您确实删除了数据库,最好将数据库的副本保存在其他位置,这样就不会丢失它。

答案 3 :(得分:0)