Я понял, что Apple намекает в их документации . Это на самом деле очень легко, но долгий путь, прежде чем он станет очевидным. Я проиллюстрирую объяснение на примере. Исходная ситуация такова:
Модель данных версии 1
Это модель, которую вы получаете, когда создаете проект с помощью шаблона «приложение на основе навигации с основным хранилищем данных». Я скомпилировал его и сделал несколько сложных ударов с помощью цикла for, чтобы создать около 2 тыс. Записей с разными значениями. Там мы идем 2000 событий со значением NSDate.
Теперь мы добавим вторую версию модели данных, которая выглядит следующим образом:
Модель данных версии 2
Разница в том, что сущность Event исчезла, и у нас есть две новые. Тот, который хранит метку времени как double
, а второй, который должен хранить дату как NSString
.
Цель состоит в том, чтобы передать все Версия 1 События двум новым объектам и преобразовать значения в процессе миграции. Это приводит к удвоению значений каждого из них как отдельного типа в отдельном объекте.
Для миграции мы выбираем миграцию вручную, и это мы делаем с моделями сопоставления. Это также первая часть ответа на ваш вопрос. Мы сделаем миграцию в два этапа, потому что миграция записей 2k занимает много времени, и мы хотим, чтобы объем памяти оставался низким.
Вы могли бы даже пойти дальше и разделить эти модели отображения дальше, чтобы перенести только диапазоны сущностей. Скажем, у нас есть миллион записей, это может привести к краху всего процесса. Можно сузить выбранные объекты с помощью предиката Filter .
Вернуться к нашим двум картографическим моделям.
Мы создаем первую модель отображения следующим образом:
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.
Наконец, нам нужно запустить миграцию. Я пока пропущу шаблонный код. Если вам это нужно, я выложу здесь. Его можно найти по адресу Настройка процесса миграции Это всего лишь слияние первых двух примеров кода. Третья и последняя часть будут изменены следующим образом: Вместо использования метода класса NSMappingModel
class mappingModelFromBundles:forSourceModel:destinationModel:
мы будем использовать initWithContentsOfURL:
потому что метод класса вернет только одну, может быть, первую найденную модель отображения в комплекте.
Теперь у нас есть две модели отображения, которые можно использовать на каждом проходе цикла, и отправить метод migrate в менеджер миграции. Вот и все.
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
в связке.
Должно быть указано хранилище назначения, и оно не должно быть хранилищем источника. После успешной миграции вы можете удалить старую и переименовать новую.
Я сделал некоторые изменения в модели данных после создания моделей отображения, это привело к некоторым ошибкам совместимости, которые я мог исправить только путем воссоздания моделей отображения.