Я з’ясував, на що натякає Apple у своїй документації . Насправді це дуже просто, але довгий шлях, перш ніж це стане очевидним. Поясню пояснення на прикладі. Початкова ситуація така:
Модель даних Версія 1
Це модель, яку ви отримуєте, створюючи проект за допомогою шаблону "Додаток на основі навігації з основним сховищем даних". Я скомпілював його та зробив важкі удари за допомогою циклу for, щоб створити близько 2 тисяч записів із різними значеннями. Там ми проводимо 2000 подій зі значенням NSDate.
Тепер ми додаємо другу версію моделі даних, яка виглядає так:
Модель даних Версія 2
Різниця полягає в тому, що сутності «Подія» вже немає, і у нас є дві нові. Один, який зберігає мітку часу як a, 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
{
NSManagedObject *newObject =
[NSEntityDescription insertNewObjectForEntityForName:[mapping destinationEntityName]
inManagedObjectContext:[manager destinationContext]];
NSDate *date = [sInstance valueForKey:@"timeStamp"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[newObject setValue:[dateFormatter stringFromDate:date] forKey:@"printedDate"];
[dateFormatter release];
[manager associateSourceInstance:sInstance withDestinationInstance:newObject forEntityMapping:mapping];
return YES;
}
Заключний крок: сама міграція
Я пропущу частину налаштування другої моделі зіставлення, яка майже ідентична, просто timeIntervalSince1970, що використовується для перетворення NSDate у подвійний.
Нарешті, нам потрібно ініціювати міграцію. Наразі я пропускаю шаблонний код. Якщо вам це потрібно, я розміщу тут. Це можна знайти в Налаштуванні процесу міграції, це лише об’єднання перших двох прикладів коду. Третя і остання частина буде модифікована наступним чином: Замість використання методу NSMappingModel
класу 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
пучку.
Потрібно надати цільовий магазин, який не повинен бути сховищем джерел. Ви можете після успішної міграції видалити стару та перейменувати нову.
Я вніс деякі зміни в модель даних після створення моделей зіставлення, це призвело до деяких помилок сумісності, які я міг вирішити лише відтворивши моделі зіставлення.