Данные ядра не могут найти модель для хранилища источников - как выглядел мой старый магазин?

Итак, во-первых, этот вопрос помог многим добиться правильного пути к правильному управлению версиями данных. Поэтому я добавил новую версию для своей модели, и теперь я пытаюсь получить автоматическую миграцию, но у меня есть проблема. Я не помню, как выглядела моя старая версия! Я пытаюсь запустить приложение на своем телефоне, но я некоторое время использовал симулятор и внес несколько изменений в схему. Версия на телефоне довольно давно. Поэтому каждый раз, когда я пытаюсь изменить старую версию на то, что я думаю, есть на телефоне, но я все равно получаю ошибку "не могу найти модель для источника". Я предполагаю, что это неправильно, потому что я неправильно использовал старую схему.

Есть ли способ узнать, как выглядит схема на телефоне? Если это не так, как я могу просто стереть хранилище sqlite с телефона, чтобы начать с версии 1?

Ответ 1

Я разрывал волосы над ошибкой "Не могу найти модель для источника" за целый день. Вот выработка ответа learner2010 для гуглеров:

  • Ваш хэш-адрес базы данных sqlite ДОЛЖЕН соответствовать одному из мамы или мама, созданного вашим xcdatamodel при создании вашего приложения. Вы можете увидеть хеши в 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 model to make sure they match one of the entries in the momd directory 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;   
}

Ответ 2

Сообщение об ошибке означает, что он не может найти файл .mom скомпилированных моделей для существующего хранилища. Core Data ищет точную версию .mom, которая настроила хранилище. Файл .mom сообщает командам Core Data, как сопоставить сериализованные данные в файле в объектах. Без этого файла модели он не знает, как перенести хранилище на новую модель, потому что он не знает, какие данные передаются с каждым свойством сущности или объекта.

Я видел это только один раз, а IIRC причина в том, что новый .mom файл имел то же имя и местоположение, что и старый. Когда приложение было обновлено, старый файл .mom был перезаписан.

Попробуйте изменить имя нового файла модели и посмотреть, поможет ли это. Если нет, нам, вероятно, понадобится более подробная информация о том, что вы делаете.

Ответ 3

У меня была такая же проблема. Я решил это, удалив мою старую базу данных и снова создав проект. Я не уверен, что это правильный путь. По-видимому, это указывает на неправильный файл базы данных. В моем случае я создавал простой проект с использованием основных данных и пытался выполнить миграцию. Когда я создал новую модель с дополнительным столбцом и построил свой проект, было 2 файла - coreData.sqlite и coreData ~.sqlite... Так что я чувствую, что неправильная база данных указывает на и, следовательно, на ошибку. Когда я удалил базу данных и снова построил проект, он отлично сработал у меня.

Если вы удалите базу данных, лучше сохранить копию базы данных в другом месте, чтобы вы ее не потеряли.