Core Data неправильно выбирает модель сопоставления

#ios #core-data

#iOS #core-data

Вопрос:

Я работаю над приложением, которое использует Core Data для управления своими постоянными данными. Мы вносили достаточно сложные изменения в схему, из-за чего облегченные автоматические миграции несовместимы, и поэтому вместо этого мы используем миграции модели, определенные вручную.

Моя проблема в том, что когда я вызываю addPersistentStoreWithType:configuration:URL: options: ошибка: с установленным параметром NSMigratePersistentStoresAutomaticallyOption, выбирается неправильная модель сопоставления. Если я вызываю mappingModelFromBundles:forSourceModel:destinationModel: вручную, я получаю ту же самую (неправильную) возвращаемую модель сопоставления.

У нас есть три версии модели данных: 1.0, 1.1 и 1.1.1. Мы определили три модели отображения: 1.0-1.1, 1.0-1.1.1 и 1.1-1.1.1. Версия данных в приложении 1.1 Текущая версия данных 1.1.1 Выбранная модель отображения 1.0-1.1.1. Это приводит к удалению и повторному созданию таблицы, которая была добавлена в 1.1, с потерей данных.

Когда я использую -com.apple.Параметр командной строки CoreData.MigrationDebug 1, он выводит отладочную информацию для процесса обновления. В нем отображаются все таблицы в текущем наборе данных (включая добавленную в 1.1) и их хэши. Затем он показывает исходные и целевые хэши для модели отображения 1.0-1.1.1, указывает, что это совместимая модель отображения, и никогда не проверяет какие-либо другие.

Если я использую [NSMappingModel initWithContentsOfURL] для конкретного выбора модели сопоставления 1.1-1.1.1, я получаю желаемое поведение.

Я в некоторой растерянности, поскольку мне кажется, что он не должен быть готов использовать модель сопоставления, где исходный хэш не соответствует хэшу текущего набора данных.

Вот некоторые выбранные выходные данные, показывающие, что таблица, которая сбрасывается, находится в базе данных, но не в модели сопоставления. Я проверил, что единственное различие между хэшем данных 1.1 и хэшем данных 1.0 заключается в том, что в 1.1 была добавлена таблица.

 2014-07-03 23:03:20.547 XXX[50242:60b] CoreData: annotation: (migration)     will attempt automatic schema migration
2014-07-03 23:03:21.212 XXX[50242:60b] CoreData: annotation: (migration) looking for mapping model with source hashes: 
{
    XXX = <77891dab a2acaff3 e52f71e3 0f8c3d5f eee99f43 f549e3fc 72eddd29 b2af83fd>;
    XXX = <6049b4fb 7d8c4e5f 63149e35 5abfd274 1264c2f9 76d13cf3 cc69a23a e29edac8>;
    XXX = <cf3bd61a 71c2838c 421c6e50 41abd013 b0c153cb b165a0e6 21d5c352 f29b5743>;
    XXX = <fd8c6be5 b97b3827 455a620c 3e6ff6e9 e2e09afd 472b9cbf 07d11e29 d5a52159>;
    XXX = <6cf5aac1 67ead46a fbaf8450 11c2c0b9 dcc1e2ae dd3bbf86 06d09b78 4d4b6bbe>;
    XXX = <09942be7 56f82126 d48a90b2 e6cf08e7 1fe9c091 1ee7fec8 8d426ca4 a00af268>;
    XXX = <40462ca4 098ae4d2 d3e8e7cf a55bc7df ca58c8c9 3aaf8d94 b681080c 63e5683b>;
    XXX = <dce53740 e8aba89f ac8180b4 0f297821 d09734a1 8ea3c344 8cb9dd6c d3baf645>;
    XXX = <c9f7a2e3 13518dac 5ae5209d 239d1c11 0fd3f11f 5366b7d4 fd3a97d3 3e3d41d1>;
    XXX = <e5bf6c2f c0c9d818 e1b4e2cc 9b7a92e3 2cd6bed4 5a98e6c3 53619376 9b3951ef>;
    XXX = <5431c046 d30e7f32 c2cc8099 58add1e7 579ad104 a3aa8fc4 846e97d7 af01cc79>;
    XXX = <3aef2c65 c9647274 1302fe8a fdca7ce6 cb87c7df 2751a19f bd946707 c8244729>;
    THIS_ONE = <1bb3a3b9 857bcdf2 ca573238 a86672b8 0486929e ed0357c8 72879022 e12efe37>;
    XXX = <9f627411 6f8c0891 3693eab1 aa45cbd3 0143b28e 1e3584da e6ea2867 554a26ad>;
    XXX = <9def8e9f 14dfb358 b5694bce 77759b7c c1901fe1 3e3a163b 80061b51 268089a8>;
    XXX = <06d0b355 4fb4ff4b 0adf05d4 8ce0378c 4aa156e9 a09c8a16 a82d8376 1fd2f929>;
    XXX = <fb9db76f 350ad944 88e1cf5d 15aaca9c 230355f9 13a2dace 62d5e4fb 0a2ecd7a>;
    XXX = <715d9149 7aea98db fdb3a2fa e1682e12 dfe8f63a d09aac57 301be349 91fffd44>;
    XXX = <002c8d92 8bc08e6c eb34fe0c a10ef78e ed050a8e 17a86e63 9911adb8 e2c36df1>;
    XXX = <362ea015 28a5c834 47b125c1 c460dd62 f0172785 e024b8aa 17dc544f 66871077>;
    XXX = <bd06507d f33ee72d d6bba2d5 c29eb8c5 1f87568b 186ab250 7312c0ec 6f2cd09c>;
    XXX = <22ff0e46 f56dbc7a e8e92cf6 9090a451 742517ff 7d29838d 0cd41e9e a3615134>;
    XXX = <ec7834a0 987c4c5f df40ade2 73075b11 e329a018 94fe47ca 08f7c9ed 95bb4da1>;
    XXX = <c3feffd4 8d223692 d314720a 4496b787 871db7d0 31097cff e1225b9b 6275e613>;
    XXX = <e5e3c8aa 5267d778 9fd62dc5 884ef416 5f836890 d82fed79 efd3796d bcb58503>;
    XXX = <6705e1bf cac0c2ed b040b64f ca1f6e6e 74332890 907ec136 7a99606c e116a946>;
    XXX = <856a10a5 18de663a 1860ea0d c0bd9295 769e4a42 99420fb5 02314b22 f39fe1a4>;
    XXX = <35f6c30f a146166c 6e132297 bf463c59 756b8071 49aae2d7 ec6b6de8 fb7f7300>;
    XXX = <ec2a9e60 6ae28042 a62429e4 b0ec5939 3734e0ac 9a919421 a9fbede2 031b0bf6>;
    XXX = <b08fbebb 9100df77 5aba3640 c8237a5b 4ddbed50 fb6cb28c 439c7e37 9b2ccb4a>;
    XXX = <95c8cfb8 a1aafabc 90a9231b 0ef15d85 10e30393 5cfd4921 4db4a12f 511c8977>;
    XXX = <4e0fcdb8 4fbf9aa3 684875aa c54a4c5d c02020b2 d29212e4 587069d2 eed3aa31>;
    XXX = <ad580044 b972d6ab df963bda ad071ba5 9c82aab5 4007f377 bf8858fe b9bc6274>;
    XXX = <4fc9af50 0722da5d 18e0b755 63cf2a04 88e8b2d3 e8196ec2 375171b1 ce40fb4e>;
}

2014-07-03 23:03:21.231 XXX[50242:60b] CoreData: annotation: (migration) checking mapping model /Users/jonathan/Library/Application Support/iPhone Simulator/7.1/Applications/XXX/XXX.app/1.0-1.1.1.cdm
 source hashes: 
{(
    <09942be7 56f82126 d48a90b2 e6cf08e7 1fe9c091 1ee7fec8 8d426ca4 a00af268>,
    <40462ca4 098ae4d2 d3e8e7cf a55bc7df ca58c8c9 3aaf8d94 b681080c 63e5683b>,
    <ec7834a0 987c4c5f df40ade2 73075b11 e329a018 94fe47ca 08f7c9ed 95bb4da1>,
    <b08fbebb 9100df77 5aba3640 c8237a5b 4ddbed50 fb6cb28c 439c7e37 9b2ccb4a>,
    <77891dab a2acaff3 e52f71e3 0f8c3d5f eee99f43 f549e3fc 72eddd29 b2af83fd>,
    <e5e3c8aa 5267d778 9fd62dc5 884ef416 5f836890 d82fed79 efd3796d bcb58503>,
    <4e0fcdb8 4fbf9aa3 684875aa c54a4c5d c02020b2 d29212e4 587069d2 eed3aa31>,
    <bd06507d f33ee72d d6bba2d5 c29eb8c5 1f87568b 186ab250 7312c0ec 6f2cd09c>,
    <5431c046 d30e7f32 c2cc8099 58add1e7 579ad104 a3aa8fc4 846e97d7 af01cc79>,
    <ad580044 b972d6ab df963bda ad071ba5 9c82aab5 4007f377 bf8858fe b9bc6274>,
    <c9f7a2e3 13518dac 5ae5209d 239d1c11 0fd3f11f 5366b7d4 fd3a97d3 3e3d41d1>,
    <3aef2c65 c9647274 1302fe8a fdca7ce6 cb87c7df 2751a19f bd946707 c8244729>,
    <6705e1bf cac0c2ed b040b64f ca1f6e6e 74332890 907ec136 7a99606c e116a946>,
    <856a10a5 18de663a 1860ea0d c0bd9295 769e4a42 99420fb5 02314b22 f39fe1a4>,
    <6049b4fb 7d8c4e5f 63149e35 5abfd274 1264c2f9 76d13cf3 cc69a23a e29edac8>,
    <6cf5aac1 67ead46a fbaf8450 11c2c0b9 dcc1e2ae dd3bbf86 06d09b78 4d4b6bbe>,
    <715d9149 7aea98db fdb3a2fa e1682e12 dfe8f63a d09aac57 301be349 91fffd44>,
    <e5bf6c2f c0c9d818 e1b4e2cc 9b7a92e3 2cd6bed4 5a98e6c3 53619376 9b3951ef>,
    <fb9db76f 350ad944 88e1cf5d 15aaca9c 230355f9 13a2dace 62d5e4fb 0a2ecd7a>,
    <35f6c30f a146166c 6e132297 bf463c59 756b8071 49aae2d7 ec6b6de8 fb7f7300>,
    <9def8e9f 14dfb358 b5694bce 77759b7c c1901fe1 3e3a163b 80061b51 268089a8>,
    <9f627411 6f8c0891 3693eab1 aa45cbd3 0143b28e 1e3584da e6ea2867 554a26ad>,
    <dce53740 e8aba89f ac8180b4 0f297821 d09734a1 8ea3c344 8cb9dd6c d3baf645>,
    <95c8cfb8 a1aafabc 90a9231b 0ef15d85 10e30393 5cfd4921 4db4a12f 511c8977>,
    <cf3bd61a 71c2838c 421c6e50 41abd013 b0c153cb b165a0e6 21d5c352 f29b5743>,
    <ec2a9e60 6ae28042 a62429e4 b0ec5939 3734e0ac 9a919421 a9fbede2 031b0bf6>,
    <c3feffd4 8d223692 d314720a 4496b787 871db7d0 31097cff e1225b9b 6275e613>,
    <06d0b355 4fb4ff4b 0adf05d4 8ce0378c 4aa156e9 a09c8a16 a82d8376 1fd2f929>,
    <4fc9af50 0722da5d 18e0b755 63cf2a04 88e8b2d3 e8196ec2 375171b1 ce40fb4e>,
    <002c8d92 8bc08e6c eb34fe0c a10ef78e ed050a8e 17a86e63 9911adb8 e2c36df1>,
    <fd8c6be5 b97b3827 455a620c 3e6ff6e9 e2e09afd 472b9cbf 07d11e29 d5a52159>,
    <362ea015 28a5c834 47b125c1 c460dd62 f0172785 e024b8aa 17dc544f 66871077>,
    <22ff0e46 f56dbc7a e8e92cf6 9090a451 742517ff 7d29838d 0cd41e9e a3615134>
)}

2014-07-03 23:03:21.233 XXX[50242:60b] CoreData: annotation: (migration) found compatible mapping model /Users/jonathan/Library/Application Support/iPhone Simulator/7.1/Applications/XXX/XXX.app/1.0-1.1.1.cdm
  

Я использую iOS 7.1 и Xcode 5.1.1.

Как упоминалось, единственным изменением между 1.0 и 1.1, по-видимому, является добавление таблицы. Я предполагаю, что используемая ими функция сравнения хэшей не считает это конфликтом? Моей следующей попыткой будет переименование миграций в предположении, что используется алфавитный поиск, чтобы сначала была найдена и проверена миграция 1.1-1.1.1. Кроме этого, я ожидаю, что мне придется добавить какую-то ручную логику (путем подкласса чего-либо?) чтобы заставить дополнительную таблицу рассматриваться как несоответствие схемы.

Комментарии:

1. Я был в такой же ситуации и не смог заставить автоматический выбор работать. В конце концов я написал метод выбора модели сопоставления вручную. Это заняло меньше времени, чем все время, потраченное на отладку автоматического выбора. Одна вещь, которую следует порекомендовать: убедитесь, что модели сопоставления точно соответствуют их соответствующей модели данных.

2. Спасибо, Олаф. Есть ли у вас какие-либо ссылки на то, как реализовать метод ручного выбора?

3. Извините, у меня нет ни одной ссылки, которая показывает, как. Я собрал фрагменты и затем склеил их вместе. Я опубликую ответ с некоторым примером кода

Ответ №1:

Это не ответ на вопрос, почему автоматический выбор модели сопоставления завершается с ошибкой. Как я писал в своем комментарии, я столкнулся с аналогичной проблемой и написал свой собственный (ручной) выбор модели сопоставления.

Для ручного выбора нам нужно знать, какая модель использовалась для создания текущего хранилища. Все объекты (таблицы) имеют хэш версии, поэтому этот хэш можно использовать для определения версии модели.

После запуска приложения, но перед загрузкой постоянного хранилища, выбор модели сопоставления перебирает возможные модели данных, то есть 1.0.xcdatamodel, 1.1.xcdatamodel и 1.1.1.xcdatamodel

Логика сравнивает известный хэш объекта THIS_ONE с хэшем версии, найденным в постоянном хранилище. Известный хэш версии THIS_ONE получен из файла модели данных. Если модель данных использовалась для создания хранилища, то хэши совпадают.

Таким образом, приложение перебирает список известных названий моделей (например, «1.0», «1.1» и «1.1.1») и вызывает метод сопоставления isModel:forStore: . Если возвращаемое значение равно YES , значит, мы нашли подходящую модель.

Теперь, когда мы знаем модель исходных данных, мы можем идентифицировать модель сопоставления. Следующим шагом было бы отменить фактическую миграцию, используя соответствующий файл сопоставления для migrateStoreFromURL:type:options:withMappingModel:toDestinationURL:destinationType:destinationOptions:error:

Вот метод сопоставления хэшей версий:

 -(BOOL)isModel:(NSString *)modelUsed forStore:(NSURL *)storeUrl {
        NSString *modelFound = @"unknown Model";
        NSDictionary *knownTHIS_ONEHashes = [self knownTHIS_ONEHashes];
        NSFileManager *fileManager = [NSFileManager defaultManager];

        BOOL doesExistCurrentStore = [fileManager fileExistsAtPath:[storeUrl path]];
        if (doesExistCurrentStore) {
            NSError *error = nil;
            NSDictionary *storeMetadata = [NSPersistentStoreCoordinator metadataForPersistentStoreOfType:NSSQLiteStoreType URL:storeUrl error:amp;error];

            if (nil == storeMetadata) {  // no source meta data => dont know if need to migrate
                NSLog(@"sourceMetadata is nil");
            } else {
                NSLog(@"sourceMetadata is %@", storeMetadata);
                NSDictionary *storeHashes = [storeMetadata objectForKey:@"NSStoreModelVersionHashes"];
                NSData *curentTHIS_ONEHash  = storeHashes[@"THIS_ONE"];
                for (NSString *modelName in [knownTHIS_ONEHashes allKeys]) {
                    if ([knownTHIS_ONEHashes[modelName] isEqualToData:curentTHIS_ONEHash]) {
                        NSLog(@"found matching model: %@",modelName);
                        modelFound = modelName;
                        break;
                    }
                }
            }
        } // else store does not exist so there is no need for a data migration
        return   ([modelUsed isEqualToString:modelFound]);
    }
  

Известные хэши считываются во время выполнения из моделей, присутствующих в пакете. _kModelNameVx это жестко закодированные названия моделей.

  -(NSDictionary *)knownTHIS_ONEHashes {
        NSMutableDictionary *returnDict = [NSMutableDictionary new];
        NSArray *knowModelFiles = @[_kModelNameV1, _kModelNameV2, _kModelNameV3, _kModelNameV4];
        NSString * destinationModelPath;
        NSURL * destinationModelURL;
        NSManagedObjectModel * destinationModel;
        for (NSString *singleFile in knowModelFiles) {
            destinationModelPath = [[NSBundle mainBundle] pathForResource:singleFile
                                                                   ofType:@"mom"
                                                              inDirectory:@"<xcdatamodel_name>.momd"];
            destinationModelURL = [NSURL fileURLWithPath:destinationModelPath];
            destinationModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:destinationModelURL];
            NSDictionary *modelMetadata = [destinationModel entityVersionHashesByName];
            NSData *THIS_ONEHash = [modelMetadata objectForKey:@"THIS_ONE"];
            [returnDict setValue:THIS_ONEHash forKey:singleFile];
        }
        return returnDict;
    }