Стратегия реализации

Следуйте инструкциям по реализации в этой главе, чтобы избежать распространенных ошибок и произвести инкрементно тестируемый код. Несмотря на то, что Ваше хранилище данных поддержки (и поэтому реализация Вашего инкрементного хранилища) будет уникально, инструкции ниже закладывают основу любому хранилищу. Вы будете учиться как:

Открытие инкрементного магазина

NSIncrementalStore класс является абстрактным подклассом NSPersistentStore класс, определяющий механизмы связи между Вашим пользовательским хранилищем и Базовой платформой Данных. Чтобы начать открывать магазин, создайте новый класс, наследовавшийся от NSIncrementalStore.

Необходимо зарегистрировать Ваш NSIncrementalStore класс с Базовой платформой Данных, прежде чем можно будет использовать хранилище в приложении. Персистентный координатор хранилища создает экземпляры Вашего класса по мере необходимости путем выполнения поиска с помощью строковой константы типа хранилища, которую Вы обеспечиваете. Самый простой способ гарантировать, что регистрация происходит, прежде чем Вы попытаетесь добавить пользовательское инкрементное хранилище к своему персистентному координатору хранилища, состоит в том, чтобы выполнить регистрацию в использовании iOS application:didFinishLaunchingWithOptions: и выполнять регистрацию в использовании OS X applicationDidFinishLaunching:.

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    [NSPersistentStoreCoordinator registerStoreClass:[XYZMyIncrementalStore class]
                                        forStoreType:@"XYZMyIncrementalStore"];
}

Когда Вы добавляете свое инкрементное хранилище к Вашему персистентному координатору хранилища, персистентный координатор хранилища создает экземпляр Вашего класса и просит, чтобы он выполнил основную проверку и установку путем вызова loadMetadata:.

В Вашей реализации loadMetadata:, Вы проверяете URL, используемый для создания хранилища, метаданных загрузки от отступающего хранилища данных, если это уже существует, и Вы создаете словарь метаданных объекта хранилища. Как минимум словарь метаданных должен содержать следующие две пары ключ/значение:

Если хранилище является новым, можно дополнительно принять решение не создать и установить метаданные хранилища. В этом случае основные метаданные автоматически создаются для Вас. Возвратиться NO и набор error параметр, если метаданные хранилища недопустимы. Например, возвратиться NO на двух условиях: если URL является HTTP URL, когда Ваше хранилище ожидает файл URL, или если у пользователя нет полномочий получить доступ к хранилищу.

- (BOOL)loadMetadata:(NSError *__autoreleasing *)error {
    NSURL *storeURL = [self URL];
    // ... metadata validation
    NSDictionary *metadata = @{NSStoreUUIDKey: <#key loaded from backing data store#>,
                               NSStoreTypeKey: @"XYZMyIncrementalStore"};
    [self setMetadata:metadata];
 
    return YES;
}

Кроме того, если Ваше запоминающее устройство поддерживает миграцию схемы, Вы сохраняете к своему хранилищу данных поддержки автоматически сгенерированные хеши версии модели в словаре метаданных с ключом NSStoreModelVersionHashes.. На последующих загрузках получите хеши версии модели и сохраните их в словаре метаданных. Если хеши версии модели в Вашем хранилище данных поддержки отличаются от тех возвращенных текущей моделью в Вашем приложении, персистентный координатор хранилища выполняет миграцию.

Перевод между пользовательскими уникальными идентификаторами и управляемым объектом IDs

Создайте служебные методы, отображающиеся между уникальными идентификаторами в Вашем хранилище данных поддержки и NSManagedObjectID экземпляры Вы используете для идентификации объектов в памяти. Как правило, уникальный идентификатор соответствует первичному ключу в Вашем хранилище данных. Свяжитесь NSManagedObjectID экземпляр со ссылочным объектом типа NSString или NSNumber. В то время как ссылочный объект объявляется как тип id, не используйте никакой другой тип объекта.

NSIncrementalStore обеспечивает два метода для упрощения перевода:

Создание NSManagedObjectID экземпляр так же прост как запрос его от Вашего инкрементного хранилища.

id uniqueIdentifier = ...
NSEntityDescription *entityDescription = ...
NSManagedObjectID *objectID = [self newObjectIDForEntity:entityDescription referenceObject:uniqueIdentifier];

Аналогично, получая ссылочный объект для NSManagedObjectID экземпляр так же прост.

NSManagedObjectID *objectID = ...
id referenceObject = [self referenceObjectForObjectID:objectID];

Ответ на запросы выборки

То, когда выборка или сохраняет запрос, выполняется, персистентный координатор хранилища передает запрос к Вашему инкрементному хранилищу. Сохраните запросы, покрыты Ответом для Сохранения Запросов (Создание, Обновление и Уничтожение Объектов). В этом разделе фокус находится по запросам выборки.

В наиболее распространенном случае Вы реагируете на запрос выборки с массивом NSManagedObject экземпляры. Эти экземпляры связали управляемый объект IDs, основывающийся на информации, содержавшейся в NSFetchRequest объект Вы получаете. Нет никакой потребности получить свойства на объекте прямо сейчас; Базовые Данные возвратят свойства как отказы. Упреждающая выборка свойств обсуждена в Кэшировании и Упреждающей выборке.

Поскольку запросы выборки могут быстро стать сложными, начаться путем реализации всего, что это необходимо для возврата массива каждого управляемого объекта единственного типа объекта. Обработка Модификаторов Объема и Дескрипторов Вида представляет более сложные запросы.

Персистентный координатор хранилища вызывает executeRequest:withContext:error: на Вашем инкрементном хранилище и передачах в NSPersistentStoreRequest объект. После проверки, что запрос является действительно запросом выборки, Вы бросаете запрос к NSFetchRequest введите и начните формулировать ответ.

- (id)executeRequest:(NSPersistentStoreRequest *)request withContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
    if ([request requestType] == NSFetchRequestType) {
        NSFetchRequest *fetchRequest = (NSFetchRequest *)request;
        NSEntityDescription *entity = [fetchRequest entity];
        ...

Затем, получите уникальные идентификаторы для каждого объекта указанного типа объекта от Вашего хранилища данных поддержки, создайте управляемый объект IDs из тех идентификаторов и попросите у контекста управляемых объектов с теми идентификаторами объектов. Добавьте все управляемые объекты к массиву и возвратите массив.

        ...
        NSArray *primaryKeys = ...
        NSMutableArray *fetchedObjects = [NSMutableArray arrayWithCapacity:[primaryKeys count]];
        for (NSString *primaryKey in primaryKeys) {
            NSManagedObjectID *objectID = <#your method that returns an object ID with an identifier and entity#>
            NSManagedObject *managedObject = [context objectWithID:objectID];
            [fetchedObjects addObject:managedObject];
        }
 
        return fetchedObjects;
    }
}

Контрольная точка

В этой точке Ваше инкрементное хранилище может реагировать на запросы выборки с отказами.

Установка: Создайте простой Базовый Стек данных с одним объектом, и вручную предварительно заполните Ваше хранилище данных поддержки с несколькими строками данных тестирования.

Тест: Выполните простой запрос выборки на хранилище, прося все объекты единственного типа объекта. Если все подходит, контекст управляемого объекта возвращает массив NSManagedObject экземпляры с доступным объектом IDs. Все значения на этих управляемых объектах появляются как отказы (и повысьте исключение, если получено доступ).

Что такое отказ?

Сбой (см. Faulting и Uniquing в Базовом Руководстве по программированию Данных) допускает увеличенную гибкость в использовании памяти путем задержки материализации значений свойств, пока они не необходимы пользователю. Посмотрите рисунок 1-1, показывающий логику, используемую Базовыми Данными и Вашим инкрементным хранилищем, когда к свойству получают доступ.

  Материализация Свойства рисунка 1-1

Когда управляемый объект сначала возвращается — как часть набора результатов от executeRequest:withContext:error:— это - отказ. В некоторой более поздней точке пользователь может попытаться получить доступ к свойствам того объекта, в который Вызовы данных Ядра точки newValuesForObjectWithID:withContext:error: или newValueForRelationship:forObjectWithID:withContext:error:, или оба, для объекта. Рисунок 1-2 показывает действия, инициировавшие методы Вашего инкрементного хранилища для выполнения.

  Материализация Объекта рисунка 1-2

Выполнение отказов атрибута

Когда отказ был запущен в управляемый объект, персистентный координатор хранилища отправляет Ваше инкрементное хранилище a newValuesForObjectWithID:withContext:error: сообщение, просящее, чтобы хранилище получало значения для атрибутов на управляемом объекте. Персистентный координатор хранилища использует эти значения для выполнения отказов. Координатор ожидает, что Вы возвратите объект типа NSIncrementalStoreNode это инкапсулирует значения атрибута, даваемые сбой в память. Создайте инкрементный узел хранилища с NSDictionary объект, содержащий пары ключ/значение, где ключи являются именами свойств на Данных Ядра объекта, дает сбой в.

- (NSIncrementalStoreNode *)newValuesForObjectWithID:(NSManagedObjectID *)objectID withContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
    NSString *uniqueIdentifier = <#your method for retrieving a unique identifier from an object ID#>
    // Retrieve the values and version for the object with the specified unique identifier from your backing store
    NSDictionary *values = ...
    uint64_t version = ...
    NSIncrementalStoreNode *node = [[NSIncrementalStoreNode alloc] initWithObjectID:objectID withValues:values version:version];
 
    return node;
}

Здесь, инкрементный номер версии узла хранилища соответствует версии данных, поскольку это существовало, когда это было в последний раз выбрано. Номер версии используется персистентным координатором хранилища, чтобы обнаружить и обработать конфликты слияния. Номер версии должен быть сохранен записью и постепенно увеличен каждый раз, когда сохраняется запись.

Изучить, как ответить с на - одно использование отношений newValuesForObjectWithID:withContext:error:, посмотрите Выполнение К - Отказы Отношения.

Контрольная точка

В этой точке Ваше инкрементное хранилище может реагировать на запросы выборки и выполнить отказы атрибута.

Установка: Создайте простой Базовый Стек данных с одним объектом, и вручную предварительно заполните Ваше хранилище данных поддержки с несколькими строками данных тестирования. Полагайте, что создание табличного представления базировало приложение для отображения данных визуально.

Тест: Выполните запрос выборки, зарегистрируйте объекты, возвращенные, и распечатайте их значения. Вы заметите, что просто выполнение запроса выборки не заставляет отказов значения быть запущенными; необходимо попытаться проверить объект для Базовых Данных для сбоя в значениях атрибута.

Обработка модификаторов объема и дескрипторов вида

Вы используете модификаторы объема, и дескрипторы вида, чтобы ограничить и упорядочить результаты возвратились из выборки. Для самой большой производительности Ваше инкрементное хранилище должно разгрузить как можно больше работы к отступающему хранилищу данных. Когда Ваше запоминающее устройство не может обработать модификатор объема или дескриптор вида, можно воздействовать на результаты в памяти.

Выполнять выборку или использовать NSFetchedResultsController объект, Ваш инкрементный класс хранилища должен поддерживать дескрипторы вида по запросу выборки. Дескрипторы вида описывают свойство на объекте, чтобы соответствовать и указать, должны ли результаты быть в порядке по возрастанию или порядке по убыванию. Однако Вы не должны пытаться поддерживать каждый дескриптор вида и модификатор объема. Скорее разработайте свое хранилище вокруг требований Вашего приложения.

Модификаторы объема изменяют набор результатов путем ограничения, какие объекты определенного объекта могут быть возвращены — или путем ограничения количества или путем осуществления предиката. Можно читать больше о форматировании предиката в Руководстве по программированию Предиката и изучить, как проанализировать предикаты в Парсинге Предикатов.

Контрольная точка

Можно теперь выполнить запросы выборки с простыми дескрипторами вида и определить объем модификаторов.

Установка: Создайте простой Базовый Стек данных с одним объектом, и вручную предварительно заполните Ваше хранилище данных поддержки с несколькими строками данных тестирования.

Тест: Создайте запросы выборки с дескрипторами вида и предикатами. Проверьте, что результаты должным образом отфильтрованы и возвращены в правильном порядке.

Ответ для сохранения запросов (Создание, обновление и уничтожение объектов)

Когда контекст управляемого объекта проинструктирован для сохранения, он сообщает своему персистентному координатору хранилища. Координатор работает с контекстом управляемого объекта для сортировки объектов в памяти в четыре набора:

Координатор хранилища отбрасывает набор объектов, не изменившихся, и отправляет три остающихся набора в Ваше инкрементное хранилище.

Особый случай: создание объектов

Объекты, создаваемые в памяти контекстом управляемого объекта, являются первым присвоенным временным объектом IDs так, чтобы они могли быть однозначно определены, не включая персистентное хранилище. Когда контекст сохраняется, однако, персистентный координатор хранилища просит, чтобы Ваше инкрементное хранилище обеспечило постоянные идентификаторы объектов для этих объектов.

Во-первых, персистентный координатор хранилища вызывает obtainPermanentIDsForObjects:error: с набором недавно создаваемых управляемых объектов. Это - шанс Вашего хранилища запросить постоянные идентификаторы объектов от Вашего хранилища данных поддержки. Затем координатор вызывает executeRequest:withContext:error: обработать каждую транзакцию.

- (NSArray *)obtainPermanentIDsForObjects:(NSArray *)array error:(NSError *__autoreleasing *)error {
    NSMutableArray *objectIDs = [NSMutableArray arrayWithCapacity:[array count]];
    for (NSManagedObject *managedObject in array) {
        NSManagedObjectID *objectID = <#request a primary key from your backing data store and use your method to convert the key into an NSManagedObjectID#>
        [objectIDs addObject:objectID];
    }
 
    return objectIDs;
}

Обработка сохраняет запросы

После того, чтобы просить, чтобы Ваше инкрементное хранилище обеспечило постоянные идентификаторы объектов для недавно созданных объектов, персистентный координатор хранилища тогда вызывает executeRequest:withContext:error:. Координатор хранилища передает в NSSaveChangesRequest объект, содержащий вставку, обновление, и, удаляет наборы транзакции. Не забудьте устанавливать номер версии в один для недавно вставленных объектов и постепенно увеличивать номер версии обновленных объектов для оптимистической блокировки. Четвертый набор, lockedObjects, содержит объекты, не изменившиеся, но отмеченные для оптимистической блокировки. Необходимо постепенно увеличить номер версии для этих объектов также. Для больше при обработке оптимистических отказов блокировки, посмотрите Оптимистическую Блокировку.

- (id)executeRequest:(NSPersistentStoreRequest *)request withContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
    if ([request requestType] == NSFetchRequestType) {
        ...
    } else if ([request requestType] == NSSaveRequestType) {
        NSSaveChangesRequest *saveRequest = (NSSaveChangesRequest *)request;
        NSSet *insertedObjects = [saveRequest insertedObjects];
        NSSet *updatedObjects = [saveRequest updatedObjects];
        NSSet *deletedObjects = [saveRequest deletedObjects];
        NSSet *optLockObjects = [saveRequest lockedObjects];
 
        // ... Perform any operations on your backing data store needed to persist the changes. set and increment version numbers.
 
        return @[];
    }
}

После сохранения изменений в Вашем хранилище данных поддержки возвратите пустой массив для выражения успеха.

Контрольная точка

Можно теперь создать, удалить и внести изменения в новые управляемые объекты.

Установка: Создайте простой Базовый Стек данных с двумя объектами, и вручную предварительно заполните Ваше хранилище данных поддержки с несколькими строками данных тестирования.

Тест: Создайте несколько управляемых объектов, установите их свойства и сохраните их связанный контекст управляемого объекта. Перевыборка, чтобы гарантировать, что это возражает, была успешно сохранена.

Выполнение к - отказы отношения

Базовые Данные представляют - отношения между объектами при помощи NSManagedObjectID экземпляры. Путем связи управляемых объектов с помощью управляемого объекта IDs вместо того, чтобы иметь отношение непосредственно к другим управляемым объектам, Базовые Данные не должны давать сбой во всех наборах связанных объектов.

Если отступающее хранилище данных делает к - отношения легко доступный, Ваше инкрементное хранилище возвращается к - отношения в newValuesForObjectWithID:withContext:error:. Если Ваш App Store и хранилище данных более эффективны, когда вся выборка отношения задерживается, Ваше хранилище может вместо этого возвратить их в newValuesForRelationship:forObjectWithID:withContext:error:, который обсужден в следующем разделе.

Хранилище к - отношения как уникальные идентификаторы рядом со значениями объекта в Вашем хранилище данных поддержки и получает обоих в единственном запросе. Переведите уникальные идентификаторы в идентификаторы объектов и замените свои уникальные идентификаторы идентификаторами объектов в словаре значений.

- (NSIncrementalStoreNode *)newValuesForObjectWithID:(NSManagedObjectID *)objectID withContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
    ...
    NSDictionary *values = ...
    // ... Convert raw unique identifiers for to-one relationships into NSManagedObjectID instances
    ...
    NSIncrementalStoreNode *node = [[NSIncrementalStoreNode alloc] initWithObjectID:objectID withValues:values version:version];
 
    return node;
}

Контрольная точка

Можно теперь создать и сохраниться к - отношения между двумя объектами.

Установка: Создайте простой Базовый Стек данных с двумя объектами, и вручную предварительно заполните Ваше хранилище данных поддержки с несколькими строками данных тестирования.

Тест: Создайте к - одно отношение между Вашими двумя объектами. Тест, который можно создать, удаляет и сохраняет отношение между двумя управляемыми объектами.

Выполнение к - много отказов отношения

К - много отношений между объектами представлены наборами управляемого объекта IDs. Путем связи объектов с наборами идентификаторов объектов, а не непосредственно к наборам объектов, Базовые Данные не должны давать сбой во всем графе объектов. К - много отношений являются обычно более дорогими для запросов для и могут потенциально использовать большие объемы памяти. Поэтому Базовые Данные дают сбой в к - много отношений отдельно от к - отношения и значения, в newValuesForRelationship:forObjectWithID:withContext:error:.

Используйте отношение name метод для определения, в котором дается сбой отношение. Запросите уникальные идентификаторы на это отношение от Вашего запоминающего устройства и возвратите управляемые объекты, представляющие объекты.

- (id)newValueForRelationship:(NSRelationshipDescription *)relationship forObjectWithID:(NSManagedObjectID *)objectID withContext:(NSManagedObjectContext *)context error:(NSError *__autoreleasing *)error {
    NSString *relationshipName = [relationship name];
    NSArray *objectIDs = <#retrieve the object ID(s) for the relationship from your backing data store#>
    return objectIDs;
}

Возвратите набор управляемого объекта IDs для к - многие отношение в NSArray объект. Этот подход также позволяет Вам поддерживать упорядоченный отношения.

При контакте с отношениями можно заметить, что явно не обрабатывается обратная связь. Скорее отношения все выражены в одном направлении. Поэтому двунаправленное many-many отношение не более трудно обработать, чем связь «один ко многим».

Контрольная точка

Можно теперь создать и сохраниться к - много отношений между объектами.

Установка: Создайте простой Базовый Стек данных с двумя объектами, и вручную предварительно заполните Ваше хранилище данных поддержки с несколькими строками данных тестирования.

Тест: Добавьте к - многие отношение как обратная связь Вашего существующего к - одно отношение между этими двумя объектами в Вашей модели. Протестируйте возможность своего инкрементного хранилища добавить и удалить объекты в отношении, а также сохраниться к - многие отношение с Вашим хранилищем данных поддержки.

Сообщение об ошибке

Базовые Данные вперед ошибки от Вашего инкрементного хранилища до создателя запроса. Ваше инкрементное хранилище должно быть подготовлено сообщить о внутренних ошибках, а также проблемах со свойствами запроса. Вместо того, чтобы повышать исключение, обычно достаточно возвратиться nil или NO как надлежащий и обеспечивают NSError объект с ясным, применимым на практике сообщением об ошибке.