Пример или объяснение миграции основных данных с несколькими проходами?

Моему приложению для iPhone необходимо перенести основное хранилище данных, а некоторые базы данных довольно большие. В документации Apple предлагается использовать« несколько проходов »для переноса данных для уменьшения использования памяти. Однако документация очень ограничена и не очень хорошо объясняет, как это сделать на самом деле. Может ли кто-нибудь указать мне на хороший пример или подробно объяснить процесс, как на самом деле это осуществить?


person Jason    schedule 13.05.2011    source источник
comment
у вас действительно были проблемы с памятью? Является ли ваша миграция легковесной или вы хотите использовать NSMigrationManager?   -  person Nick Weaver    schedule 19.05.2011
comment
Да, консоль GDB показала, что были предупреждения о памяти, а затем приложение вылетало из-за ограниченной памяти. Я пробовал как облегченную миграцию, так и NSMigrationManager, но сейчас я пытаюсь использовать NSMigrationManager.   -  person Jason    schedule 19.05.2011
comment
хорошо, не могли бы вы подробнее рассказать, что изменилось?   -  person Nick Weaver    schedule 19.05.2011
comment
наконец, я узнал, прочтите мой ответ.   -  person Nick Weaver    schedule 24.05.2011
comment
Привет, Джейсон, не могли бы вы исправить то же самое в вопросе?   -  person Yuchen    schedule 02.03.2015


Ответы (3)


Я выяснил, на что Apple намекает в их документация. На самом деле это очень просто, но предстоит пройти долгий путь, прежде чем станет очевидным. Я проиллюстрирую объяснение на примере. Исходная ситуация такова:

Версия модели данных 1

введите описание изображения здесь введите описание изображения здесь

Это модель, которую вы получаете, когда создаете проект с шаблоном «приложение на основе навигации с основным хранилищем данных». Я скомпилировал его и сделал несколько попыток с помощью цикла for, чтобы создать около 2k записей с разными значениями. Теперь мы переходим к 2.000 событий со значением NSDate.

Теперь мы добавляем вторую версию модели данных, которая выглядит так:

введите описание изображения здесь

Версия модели данных 2

Разница в том, что сущность Event исчезла, а у нас появилось две новых. Один хранит метку времени как double, а второй - дату как NSString.

Цель состоит в том, чтобы перенести все события версии 1 в два новых объекта и преобразовать значения во время миграции. Это приводит к удвоению значений каждого отдельного типа в отдельном объекте.

Чтобы выполнить миграцию, мы выбираем миграцию вручную, и это мы делаем с моделями сопоставления. Это тоже первая часть ответа на ваш вопрос. Мы выполним миграцию в два этапа, потому что перенос 2 КБ записей занимает много времени, и мы хотим, чтобы объем памяти был низким.

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

Вернемся к нашим двум моделям картографии.

Мы создаем первую модель отображения следующим образом:

1. Новый файл -> Ресурс -> Модель сопоставления введите описание изображения здесь

2. Выберите имя, я выбрал StepOne

3. Установить исходную и целевую модель данных

введите описание изображения здесь

Отображение модели, шаг первый

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Многопроходная миграция не требует политики миграции настраиваемых сущностей, однако мы сделаем это, чтобы получить более подробную информацию для этого примера. Итак, мы добавляем настраиваемую политику к сущности. Это всегда подкласс NSEntityMigrationPolicy.

введите описание изображения здесь

Этот класс политики реализует некоторые методы для выполнения нашей миграции. Однако в данном случае это просто, поэтому нам придется реализовать только один метод: createDestinationInstancesForSourceInstance:entityMapping:manager:error: < / а>.

Реализация будет выглядеть так:

StepOneEntityMigrationPolicy.m

#import "StepOneEntityMigrationPolicy.h"


@implementation StepOneEntityMigrationPolicy

- (BOOL)createDestinationInstancesForSourceInstance:(NSManagedObject *)sInstance 
                                      entityMapping:(NSEntityMapping *)mapping 
                                            manager:(NSMigrationManager *)manager 
                                              error:(NSError **)error
{
    // Create a new object for the model context
    NSManagedObject *newObject = 
        [NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName] 
                                      inManagedObjectContext:[manager destinationContext]];

    // do our transfer of nsdate to nsstring
    NSDate *date = [sInstance valueForKey:@"timeStamp"];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
    [dateFormatter setDateStyle:NSDateFormatterMediumStyle];    

    // set the value for our new object
    [newObject setValue:[dateFormatter stringFromDate:date] forKey:@"printedDate"];
    [dateFormatter release];

    // do the coupling of old and new
    [manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];

    return YES;
}

Последний шаг: сама миграция

Я пропущу часть настройки второй модели сопоставления, которая почти идентична, только timeIntervalSince1970, используемый для преобразования NSDate в double.

Наконец, нам нужно запустить миграцию. Я пока пропущу шаблонный код. Если нужно, выложу здесь. Его можно найти по адресу Настройка процесса миграции это просто слияние первых двух примеров кода. Третья и последняя часть будет изменена следующим образом: Вместо использования метода класса _6 _ class mappingModelFromBundles:forSourceModel:destinationModel: мы будем использовать _ 8_, потому что метод класса вернет только одну, возможно, первую найденную модель сопоставления в пакете .

Теперь у нас есть две модели сопоставления, которые можно использовать на каждом проходе цикла и отправить метод миграции диспетчеру миграции. Вот и все.

NSArray *mappingModelNames = [NSArray arrayWithObjects:@"StepOne", @"StepTwo", nil];
NSDictionary *sourceStoreOptions = nil;

NSURL *destinationStoreURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"CoreDataMigrationNew.sqlite"];

NSString *destinationStoreType = NSSQLiteStoreType;

NSDictionary *destinationStoreOptions = nil;

for (NSString *mappingModelName in mappingModelNames) {
    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:mappingModelName withExtension:@"cdm"];

    NSMappingModel *mappingModel = [[NSMappingModel alloc] initWithContentsOfURL:fileURL];

    BOOL ok = [migrationManager migrateStoreFromURL:sourceStoreURL
                                               type:sourceStoreType
                                            options:sourceStoreOptions
                                   withMappingModel:mappingModel
                                   toDestinationURL:destinationStoreURL
                                    destinationType:destinationStoreType
                                 destinationOptions:destinationStoreOptions
                                              error:&error2];
    [mappingModel release];
} 

Примечания

  • Модель отображения заканчивается на cdm в комплекте.

  • Необходимо указать целевое хранилище, которое не должно быть исходным хранилищем. После успешной миграции вы можете удалить старую и переименовать новую.

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

person Nick Weaver    schedule 23.05.2011
comment
Черт возьми, это сложно. О чем думала Apple? - person aroth; 04.05.2012
comment
Я не знаю, но всякий раз, когда я думаю, что основные данные - это хорошая идея, я изо всех сил стараюсь найти более простое и удобное в обслуживании решение. - person Nick Weaver; 04.05.2012
comment
Спасибо! Это отличный ответ. Это кажется сложным, но не так уж и плохо, если вы выучите шаги. Самая большая проблема в том, что в документации это не объясняется так, как вам нужно. - person bentford; 15.05.2012
comment
Вот обновленная ссылка на настройку процесса миграции. Он переехал с момента написания этого поста. developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ - person user1021430; 30.06.2014
comment
@NickWeaver, как вы определяете destinationStoreURL? Вы создаете его или он создается основной системой данных в процессе миграции ???? - person dev gr; 01.08.2014
comment
Привет, @NickWeaver, не могли бы вы взглянуть на следующий пост. Речь идет об этом посте, за которым я пытался следить, но не смог выполнить оставшиеся шаги. stackoverflow.com/questions/25370222/ - person Pavan; 19.08.2014
comment
поскольку метод класса вернет только одну, может быть, первую найденную модель сопоставления в пакете, я очень не уверен в этом утверждении. Логика этого метода подразумевает, что он находит среди всех доступных моделей данных в указанных пакетах ту, которая соответствует метаданным из данного хранилища. Это эффективная версия NSPersistentStoreCoordinator.addPersistentStore, которая завершается ошибкой. Миграция будет работать без итерации по моделям сопоставления, вы можете проверить это, если хотите. - person art-divin; 28.01.2020

Эти вопросы связаны:

Проблемы с памятью при переносе больших хранилищ данных CoreData на iPhone

Многопроходная миграция основных данных по частям с iOS

Процитируем первую ссылку:

Это обсуждается в официальной документации в разделе «Несколько проходов», однако похоже, что предлагаемый ими подход состоит в том, чтобы разделить вашу миграцию по типу сущности, то есть создать несколько моделей сопоставления, каждая из которых переносит подмножество типов сущностей из полная модель данных.

person occulus    schedule 13.05.2011
comment
Спасибо за ссылки. Проблема в том, что на самом деле никто не объясняет подробно как настроить его за несколько проходов. Как мне настроить несколько моделей сопоставления, чтобы они работали эффективно? - person Jason; 14.05.2011

Предположим, ваша схема базы данных имеет 5 сущностей, например человек, студент, курс, класс и регистрация для использования стандартного вида примера, где ученик подкласса человека, класс реализует курс, а регистрация присоединяется к классу и ученику. Если вы внесли изменения во все определения таблиц, вам нужно начать с базовых классов и продвигаться вверх. Таким образом, вы не можете начать с преобразования регистраций, потому что каждая регистрационная запись зависит от наличия в ней класса и студентов. Итак, вы должны начать с миграции только таблицы Person, копирования существующих строк в новую таблицу и заполнения любых новых полей (если возможно) и удаления удаленных столбцов. Выполняйте каждую миграцию внутри пула автозапуска, чтобы после ее завершения ваша память вернулась к работе.

Как только таблица Person будет готова, вы можете преобразовать таблицу ученика. Затем перейдите к Курсу, затем к Классу и, наконец, к Таблице регистрации.

Другое соображение - это количество записей, если, например, у Person была тысяча строк, вам нужно было бы каждые 100 или около того выполнять NSManagedObject, эквивалент выпуска, который должен сообщить контексту управляемого объекта [moc refreshObject: ob mergeChanges: НЕТ]; Также установите низкий уровень таймера устаревших данных, чтобы память часто очищалась.

person PapaSmurf    schedule 21.05.2011
comment
Итак, вы по существу предлагаете иметь новую схему основных данных, которая не является частью старой схемы, и вручную копировать данные в новую схему? - person Jason; 22.05.2011
comment
-1 Вручную отображать вашу базу данных не нужно. Вы можете перенести развернутые базы данных с помощью упрощенной миграции или явных моделей MappingModels. - person bentford; 15.05.2012