Моему приложению для iPhone необходимо перенести основное хранилище данных, а некоторые базы данных довольно большие. В документации Apple предлагается использовать« несколько проходов »для переноса данных для уменьшения использования памяти. Однако документация очень ограничена и не очень хорошо объясняет, как это сделать на самом деле. Может ли кто-нибудь указать мне на хороший пример или подробно объяснить процесс, как на самом деле это осуществить?
Пример или объяснение миграции основных данных с несколькими проходами?
Ответы (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
в комплекте.Необходимо указать целевое хранилище, которое не должно быть исходным хранилищем. После успешной миграции вы можете удалить старую и переименовать новую.
Я внес некоторые изменения в модель данных после создания моделей сопоставления, это привело к некоторым ошибкам совместимости, которые я мог решить только путем воссоздания моделей сопоставления.
Эти вопросы связаны:
Проблемы с памятью при переносе больших хранилищ данных CoreData на iPhone
Многопроходная миграция основных данных по частям с iOS
Процитируем первую ссылку:
Это обсуждается в официальной документации в разделе «Несколько проходов», однако похоже, что предлагаемый ими подход состоит в том, чтобы разделить вашу миграцию по типу сущности, то есть создать несколько моделей сопоставления, каждая из которых переносит подмножество типов сущностей из полная модель данных.
Предположим, ваша схема базы данных имеет 5 сущностей, например человек, студент, курс, класс и регистрация для использования стандартного вида примера, где ученик подкласса человека, класс реализует курс, а регистрация присоединяется к классу и ученику. Если вы внесли изменения во все определения таблиц, вам нужно начать с базовых классов и продвигаться вверх. Таким образом, вы не можете начать с преобразования регистраций, потому что каждая регистрационная запись зависит от наличия в ней класса и студентов. Итак, вы должны начать с миграции только таблицы Person, копирования существующих строк в новую таблицу и заполнения любых новых полей (если возможно) и удаления удаленных столбцов. Выполняйте каждую миграцию внутри пула автозапуска, чтобы после ее завершения ваша память вернулась к работе.
Как только таблица Person будет готова, вы можете преобразовать таблицу ученика. Затем перейдите к Курсу, затем к Классу и, наконец, к Таблице регистрации.
Другое соображение - это количество записей, если, например, у Person была тысяча строк, вам нужно было бы каждые 100 или около того выполнять NSManagedObject, эквивалент выпуска, который должен сообщить контексту управляемого объекта [moc refreshObject: ob mergeChanges: НЕТ]; Также установите низкий уровень таймера устаревших данных, чтобы память часто очищалась.