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