+ [NSManagedObjectModel mergedModelFromBundles :: forStoreMetadata:] всегда возвращает ноль - PullRequest
1 голос
/ 06 апреля 2019

У меня есть модель Core Data с 15 версиями. У него есть код для постепенного перехода с текущей версии магазина на последнюю версию при запуске.

Ключом к этому является звонок на

    NSDictionary* options = @{ NSMigratePersistentStoresAutomaticallyOption : @true,
                               NSInferMappingModelAutomaticallyOption : @true };
    NSDictionary* sourceMetadata = [NSPersistentStoreCoordinator
                                        metadataForPersistentStoreOfType: inType
                                        URL: inSourceStore
                                        options: options
                                        error: outError];
    NSManagedObjectModel* model = [NSManagedObjectModel mergedModelFromBundles: @[ [NSBundle bundleForClass: [self class]] ]
                                                        forStoreMetadata: inSourceMetadata];

Но это всегда возвращает ноль, и я не уверен почему. Существующий магазин - версия 14, новая модель - версия 15.

Теперь, последнее изменение в модели было довольно тривиальным (добавление пары необязательных полей), поэтому я бы подумал, что это может автоматически вывести отображение, но это не сработало, поэтому я добавил модель отображения из от версии 14 до версии 15, используя для этого ассистента Xcode, и не внесла никаких изменений.

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

В том же духе, когда я говорю «версия 14», я имею в виду последовательную нумерацию файлов .xcdatamodel. Есть ли способ посмотреть на фактическое хранилище и определить, какая версия модели Core Data считает, что это?

Ответы [ 2 ]

1 голос
/ 06 апреля 2019

Ну, во-первых, вы, кажется, знаете, что делаете, пережив 14 миграций Core Data и все.Так что я думаю, что вы должны быть в поиске какой-то глупой ошибки типа шлепка по лбу.

Убедитесь, что [NSBundle bundleForClass: [self class]] возвращает ожидаемый пакет, который содержит каталог Contents/Resources/YourModelName.momd, и что этот каталог содержит всенеобходимых .mom файлов (по одному для каждой версии) и VersionInfo.plist файла.Мои сборки также содержат файл .omo только для последней версии.

Теперь я отвечу на ваш второй вопрос, который действительно может помочь вам ответить на ваш первый вопрос.

В этом * 1012В файле * вы найдете словарь с именем NSManagedObjectModel_VersionHashes, который, в свою очередь, содержит под-словари, по одному ключу для каждой версии.Каждый под-словарь версии содержит ключ для каждого из имен и значений вашего объекта, который представляет собой 32-байтовый (256-битный) хэш атрибутов и отношений этого объекта в этой версии.Давайте назовем это хэшами модели .

Теперь откройте файл базы данных хранилища с помощью средства просмотра SQLite или инструмента командной строки sqlite3.В этой базе данных рядом с одной таблицей для каждого объекта в модели вы увидите таблицу с именем Z_METADATA с одной строкой и тремя столбцами.Значение столбца с именем Z_PLIST вводится как двоичный объект двоичных данных.Скопируйте эти данные в файл, назовите его с расширением .plist, дважды щелкните и, удивительно, он откроется в вашем любимом редакторе списков, потому что эти данные на самом деле представляют собой строку текста, представляющую список свойств Apple в формате XML.Значение его ключа NSStoreModelVersionHashes фактически является под-словарем, который подобен подсловарю в файле VersionInfo.plist.Давайте назовем это хешами магазина .Хэш 32-битной (256-битной) версии кодируется в Base64.(Существует 44 символа Base64. Поскольку каждый символ Base64 представляет 6 битов, 44 символа могут представлять до 44 * 6 = 264 бит.)

Наконец, чтобы ответить на ваш второй вопрос, storeMetadata, переданный *На самом деле 1029 * - это Z_METADATA из магазина, который содержит эти хешей .+[NSManagedObjectModel mergedModelFromBundles:forStoreMetadata:] сравнивает эти хеши хранилища с хешами модели из каждой модели данных-кандидатов в переданном bundle и возвращает модель, у которой хэши модели сопоставьте хэши магазина для всех сущностей, без каких-либо дополнительных несопоставимых сущностей с обеих сторон.

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

0 голосов
/ 06 апреля 2019

Ах. Оказывается, я отредактировал последнюю версию модели, а не добавил новую. Вот почему никто не будет соответствовать. После того, как я вернул последнюю версию и добавил новую версию модели с изменениями, она работала нормально, даже без моделей отображения по умолчанию.

Я понял это, протестировав каждую модель на соответствие метаданным источника, используя следующее:

    NSDictionary* storeHashes = [sourceMetadata objectForKey: NSStoreModelVersionHashesKey];
    NSArray<NSURL*>* urls = [self getModelURLs];
    urls = [urls sortedArrayUsingComparator:
                    ^NSComparisonResult(id  _Nonnull obj1, id  _Nonnull obj2)
                    {
                            NSURL* s2 = obj1;
                            NSURL* s1 = obj2;
                            return [s1.lastPathComponent compare: s2.lastPathComponent options: NSNumericSearch];
                    }];

    for (NSURL* url in urls)
    {
        NSDictionary* modelHashes = [self getHashesForModelAtURL: url];

        //  Compare the hashes…

        bool matches = true;
        for (NSString* entityKey in storeHashes.allKeys)
        {
            NSString* storeHash = storeHashes[entityKey];
            NSString* modelHash = modelHashes[entityKey];
            if (![storeHash isEqual: modelHash])
            {
                NSLogDebug(@"Model %@ has mismatch on %@", url.lastPathComponent, entityKey);
                matches = false;
            }
        }

        if (matches)
        {
            NSLogDebug(@"Version matches: %@", url.lastPathComponent);
            break;
        }
    }

- (NSArray<NSURL*>*)
getModelURLs
{
    NSBundle* bundle = [NSBundle bundleForClass: [self class]];
    NSArray<NSURL*>* urls = [bundle URLsForResourcesWithExtension: @"mom" subdirectory: @"Model.momd"];
    return urls;
}

- (NSDictionary*)
getHashesForModelAtURL: (NSURL*) inURL
{
    NSManagedObjectModel* model = [[NSManagedObjectModel alloc] initWithContentsOfURL: inURL];
    NSDictionary* hashes = model.entityVersionHashesByName;
    return hashes;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...