Приклад чи пояснення міграції основних даних із кількома проходами?


85

Мій додаток iPhone повинен перенести основний сховище даних, а деякі бази даних досить великі. Документація Apple пропонує використовувати "кілька проходів" для міграції даних для зменшення використання пам'яті. Однак документація дуже обмежена і не дуже добре пояснює, як насправді це зробити. Хтось може підказати мені хороший приклад, або детально пояснити процес, як насправді це зробити?


Ви насправді мали проблеми з пам'яттю? Ваша міграція мала чи ви хочете використовувати NSMigrationManager?
Нік Вівер

Так, консоль GDB показала, що були попередження про пам’ять, а потім програма виходила з ладу через обмежену пам’ять. Я спробував як легку міграцію, так і NSMigrationManager, але зараз я намагаюся використовувати NSMigrationManager.
Джейсон,

гаразд, ви можете трохи детальніше розповісти, що змінилося?
Нік Вівер

нарешті, я дізнався, прочитайте мою відповідь.
Нік Вівер

Привіт Джейсоне, ти міг би виправити подібне у питанні?
Yuchen Zhong 02.03.15

Відповіді:


174

Я з’ясував, на що натякає 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
{
    // 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 у подвійний.

Нарешті, нам потрібно ініціювати міграцію. Наразі я пропускаю шаблонний код. Якщо вам це потрібно, я розміщу тут. Це можна знайти в Налаштуванні процесу міграції, це лише об’єднання перших двох прикладів коду. Третя і остання частина буде модифікована наступним чином: Замість використання методу 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пучку.

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

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


59
Криваве пекло, що складно. Про що думала компанія Apple?
aroth

7
Я не знаю, але щоразу, коли я вважаю, що основні дані - це гарна ідея, я намагаюся знайти простіше та ремонтопридатніше рішення.
Нік Вівер

5
Дякую! Це чудова відповідь. Це здається складним, але це не так вже й погано, коли ви вивчите кроки. Найбільша проблема полягає в тому, що документація не пише вас так.
Бентфорд,

2
Ось оновлене посилання на Налаштування процесу перенесення. З моменту написання цієї публікації це змінилося. developer.apple.com/library/ios/documentation/Cocoa/Conceptual/…
user1021430

@NickWeaver, як ти визначаєш пункт призначенняStoreURL? Ви його створюєте, або він створюється основною системою даних під час процесу міграції ????
dev gr

3

Ці питання пов’язані:

Проблеми з пам'яттю переносять великі сховища даних CoreData на iPhone

Багатопрохідна міграція основних даних фрагментами з iOS

Щоб процитувати перше посилання:

Це обговорюється в офіційній документації в розділі "Кілька проходів", проте схоже, що запропонований ними підхід полягає у розподілі міграції за типом сутності, тобто створення декількох моделей зіставлення, кожна з яких мігрує підмножину типів сутності з повна модель даних.


1
Дякую за посилання. Проблема полягає в тому, що насправді ніхто не детально пояснює, як це налаштувати за кілька проходів. Як слід налаштувати кілька моделей зіставлення, щоб вона працювала ефективно?
Джейсон

-5

Припустимо, що у вашій схемі бази даних є 5 сутностей, наприклад, людина, студент, курс, клас та реєстрація, щоб використати стандартний приклад, коли студент підкласів людина, клас реалізує курс, а реєстрація приєднується до класу та студента. Якщо ви внесли зміни до всіх цих визначень таблиць, вам доведеться починати з базових класів і підніматися. Отже, ви не можете починати з перетворення реєстрацій, оскільки кожен запис про реєстрацію залежить від наявності класу та учнів там. Отже, ви б почали з міграції лише таблиці Person, копіювання наявних рядків у нову таблицю та заповнення будь-яких нових полів (якщо це можливо) та відкидання видалених стовпців. Виконуйте кожну міграцію всередині пулу автовипуску, щоб після того, як це буде зроблено, пам’ять знову почала працювати.

Після того, як таблиця Person буде виконана, ви зможете перетворити таблицю студентів. Потім перейдіть до курсу, а потім до класу і, нарешті, до таблиці реєстрації.

Інше врахування - це кількість записів, якщо, на зразок Person було тисячу рядків, вам доведеться, кожні 100 або близько того, виконувати еквівалент NSManagedObject випуску, який повинен повідомляти контекст керованого об’єкта [moc refreshObject: ob mergeChanges: НЕМАЄ]; Також встановіть низький рівень таймера застарілих даних, щоб пам’ять часто очищалася.


Отже, ви по суті пропонуєте створити нову схему основних даних, яка не є частиною старої схеми, і скопіювати дані до нової схеми вручну?
Джейсон,

-1 Ручне відображення бази даних не потрібно. Ви можете мігрувати розгорнуті бази даних, використовуючи полегшену міграцію або з явними MappingModels.
Бентфорд,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.