Онлайн курсы по Swift

Введение

Core Data вокруг нас в течение многих лет. Он используется в тысячах приложений миллионами людей, как на iOS, так и на OS X. Core Data поддерживается Apple и очень хорошо документирован. Это зрелый фреймворк, который доказывает свою эффективность снова и снова.

Несмотря на то, что Core Data опирается в большей степени на Runtime Objective-C и искусно интегрирован с Core Foundation, вы можете его легко использовать в своих Swift-проектах. В результате, это простой в использовании фреймворк для управления объектным графом, элегантный в использовании и крайне эффективный в плане потребления памяти.

 

1. Необходимый минимум

Core Data не сложен сам по себе, но если вы новичок в разработке под iOS или OS X, то мы рекомендуем вам сначала ознакомиться с серией видео уроков, посвященных iOS-разработке. Вы освоите основы iOS-программирования и,  будете иметь достаточно базовых знаний, чтобы взяться за более сложные темы, такие как Core Data.

 

Ознакомьтесь с видео курсами:

Курс: Swift с нуля
Курс: Swift объектно-ориентированное программирование

 

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

Каждый компонент Core Data имеет конкретное предназначение и функциональность. Если вы попытаетесь использовать Core Data не так, как он был задуман, то, в конечном итоге, вы неизбежно окажетесь в бедственном положении.

 

2. Кривая обучения

Core Data может показаться сложным на первый взгляд, но после того, как вы поймете каким образом разные кусочки мозаики складываются в единое целое, его API становится интуитивно понятным и лаконичным. Именно по этой причине у большинства разработчиков возникают проблемы с этим фреймворком. Они пытаются использовать Core Data, не увидев картину в целом, они не знают как различные кусочки мозаики собрать воедино и как они связаны друг с другом.

В этой статье мы познакомим вас со стеком технологий Core Data. Поняв однажды ключевые моменты Core Data, вы почувствуете себя увереннее и даже оцените и полюбите хорошо продуманное API этого фреймворка.

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

 

3. Что такое Core Data?

Зачастую новички ошибочно считают, что Core Data работает как база данных. Мы надеемся, вы поймете, что Core Data не является базой данных, и вам не следует ждать, что он будет работать аналогичным образом.

Что же тогда Core Data, если не база данных? Core Data — это уровень модели вашего приложения в самом широком смысле слова. Это Модель в шаблоне Модель-Представление-Контроллер, который пронизывает iOS SDK.

Core Data — это не база данных вашего приложения, также это не API для сохранения данных в базе данных. Core Data — это фреймворк, который управляет графом объектов. Это так просто. Core Data может сохранять данные графа объектов путем записи их на диск, но это не является его основной целью.

 

4. Стек технологий Core Data

Как я упоминал ранее, стек технологий является сердцем Core Data. Это коллекция объектов, которые являются как-бы чехлом Core Data. Ключевыми объектами стека являются: managed object model (управляемая объектная модель), persistent store coordinator (координатор постоянного хранилища) и одного или нескольких managed object contexts (контекстов управляемого объекта). Давайте кратко рассмотрим каждый компонент.

 

NSManagedObjectModel

Управляемая объектная модель (managed object model) олицетворяет модель данных приложения. Несмотря на то, что Core Data не является базой данных, вы можете сравнить управляемую объектную модель (managed object model) со схемой базы данных, то есть, она содержит информацию о моделях или сущностях графа объекта, какие атрибуты они имеют, и как они связаны друг с другом.
Объект NSManagedObjectModel узнает о модели данных путем загрузки одного или нескольких файлов модели данных во время инициализации. Мы посмотрим как это работает чуть позже.

 

NSPersistentStoreCoordinator

Как и говорит его название, объект NSPersistentStoreCoordinator сохраняет данные на диск и гарантирует, что постоянное хранилище(-а) и модель данных являются совместимыми. Это посредник между постоянным хранилищем(-ами) и контекстом(-ами) управляемого объекта, также он заботится о загрузке и кэшировании данных. Это действительно так. Core Data имеет встроенный механизм кэширования.

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

 

NSManagedObjectContext

Объект NSManagedObjectContext управляет коллекцией объектов модели, экземплярами класса NSManagedObject. Приложение может иметь несколько контекстов управляемого объекта. Каждый контекст управляемого объекта опирается на координатор постоянного хранилища.

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

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

 

5. Изучение стека технологий Core Data

Шаг 1: Настройка проекта
Давайте рассмотрим стек технологий Core Data более подробно на примере. Создайте новый проект в Xcode, выбрав New > Project… из меню File. Выберете шаблон Single View Application в разделе iOS > Application.

 

Назовите проект Core Data, выберите Language (используемый язык) — Swift, Devices (целевые устройства) — iPhone и установите флажок с надписью Use Core Data (использовать Core Data). Скажите Xcode, где вы хотите хранить файлы проекта и нажмите кнопку Create (создать).

 

Шаг 2: Обзор
По умолчанию, Apple вставляет код, связанный с Core Data в классе делегата приложения, класс AppDelegate в нашем примере. Откройте AppDelegate.swift и давайте рассмотрим реализацию класса AppDelegate.

В самом верху AppDelegate.swift вы должны увидеть оператор import для фреймворка Core Data.

 

import UIKit
import CoreData

 

Кроме того, класс AppDelegate содержит четыре lazy (ленивых) хранимых свойства:

applicationDocumentsDirectory типа NSURL
managedObjectModel типа NSManagedObjectModel
managedObjectContext типа NSManagedObjectContext
persistentStoreCoordinator типа NSPersistentStoreCoordinator

 

Первое свойство, applicationDocumentsDirectory, является не более чем вспомогательным методом для доступа к каталогу Documents (Документы) приложения. Как вы видите, реализация довольно проста. Класс NSFileManager используется для получения расположения каталога Documents (Документы) приложения.

 

lazy var applicationDocumentsDirectory: NSURL = {
    let urls = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
    return urls[urls.count-1]
}()

 

Остальные три lazy (ленивых) хранимых свойства более интересны и непосредственно связанны с Core Data. Сначала мы рассмотрим свойство managedObjectContext.

 

Шаг 3: Контекст управляемого объекта
Этот класс вы будете использовать наиболее часто, помимо NSManagedObject, при взаимодействии с Core Data в качестве NSManagedObjectContext.

lazy var managedObjectContext: NSManagedObjectContext = {
    let coordinator = self.persistentStoreCoordinator
    var managedObjectContext = NSManagedObjectContext(concurrencyType: .MainQueueConcurrencyType)
    managedObjectContext.persistentStoreCoordinator = coordinator
    return managedObjectContext
}()

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

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

Таким образом, контекст управляемого объекта управляет коллекцией объектов модели, экземплярами класса NSManagedObject и хранит ссылку на координатор постоянного хранилища. Имейте это в виду при чтении остальной части этой статьи.

 

Шаг 4: Координатор постоянного хранилища
Как мы только что видели, свойство persistentStoreCoordinator доступно в ходе конфигурирования контекста управляемого объекта. Взгляните на реализации свойства persistentStoreCoordinator, но не позволяйте ему запугать вас. На самом деле это не так сложно.

 

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
    let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent("SingleViewCoreData.sqlite")
    var failureReason = "There was an error creating or loading the application's saved data."
    do {
        try coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options: nil)
    } catch {
        var dict = [String: AnyObject]()
        dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data"
        dict[NSLocalizedFailureReasonErrorKey] = failureReason
 
        dict[NSUnderlyingErrorKey] = error as NSError
        let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
        NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
        abort()
    }
     
    return coordinator
}()

Как правило, вам захочется хранить граф объектов Core Data на диске и реализация свойства persistentStoreCoordinator от Apple использует для этого базу данных SQLite. Это общий сценарий для приложений, использующих Core Data.

В замыкании свойства persistentStoreCoordinator мы начинаем создавать экземпляр класса NSPersistentStoreCoordinator, передав в качестве аргумента управляемую объектную модель. Мы рассмотрим свойство managedObjectModel через минуту.

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

Начнем с определения расположения хранилища на диске, используя свойство applicationDocumentsDirectory, которое мы уже видели ранее. Результат, в виде объекта NSURL передается в метод addPersistentStoreWithType(_:configuration:URL:options:) класса NSPersistentStoreCoordinator. Как и указывает имя метода, он добавляет постоянное хранилище к координатору постоянного хранилища. Этот метод принимает четыре аргумента.

Во-первых укажем тип хранилища, NSSQLiteStoreType в этом примере. Core Data также поддерживает бинарные (двоичные) хранилища (NSBinaryStoreType) и хранилище типа in-memory (NSInMemoryStoreType).

Второй аргумент сообщает Core Data какую конфигурацию использовать для постоянного хранения. Мы передали nil, тем самым сообщив Core Data, что следует использовать конфигурацию по умолчанию. Третий аргумент — это расположение хранилища, он содержится в url.

Четвертый аргумент — словарь дополнительных опций, которые позволяют нам видоизменять поведение постоянного хранилища. Позже мы пересмотрим этот аспект (в следующих статьях этой серии), а пока просто передадим nil.

Так как addPersistentStoreWithType(_:configuration:URL:options:) может привести к исключению, его вызов мы заворачиваем в оператор do-catch. При отсутствии ошибок данный метод вернет объект NSPersistentStore. Мы не сохраняем ссылку на постоянное хранилище, потому что, однажды добавив его к координатору постоянного хранилища, мы больше не будем взаимодействовать с ним.

Если при добавлении постоянного хранилища произошел сбой, что означает наличие проблемы с постоянным хранилищем приложения, то мы должны принять необходимые шаги для решения этой проблемы. Когда и почему это происходит — тема следующих статей этой серии.

В операторе catch мы регистрируем все ошибки в консоли и вызываем метод abort. Вы никогда не должны вызывать abort в рабочей среде, потому что это приведет к аварийному завершению приложения. Мы реализуем менее грубое решение позже, в этой серии статей.

 

Шаг 5: Управляемая объектная модель
Третий и последний кусочек мозаики — это управляемая объектная модель. Давайте взглянем на реализацию свойства managedObjectModel.

lazy var managedObjectModel: NSManagedObjectModel = {
    let modelURL = NSBundle.mainBundle().URLForResource("Core_Data", withExtension: "momd")!
    return NSManagedObjectModel(contentsOfURL: modelURL)!
}()

Эта реализация очень проста. Мы сохраняем расположение модели приложения в modelURL и используем его при создании экземпляра модели управляемого объекта.

Так как инициализатор, init(contentsOfURL:), возвращает optional-значение, мы принудительно «разворачиваем» (извлекаем) его перед возвратом результата. Разве это не опасно? И да и нет. Не рекомендуется принудительно «разворачивать» optional-значения. Однако, неспособность инициализировать управляемую объектную модель означает, что приложение не может найти модель данных в пакете (bundle) приложения. Если это случилось, то что-то пошло не так, причем это что-то находится за пределами контроля приложения.

В этом месте, вы, наверно, задаете себе вопрос, на какую модель указывает modelURL и что это за файл с расширением .momd. Чтобы ответить на эти вопросы, мы должны выяснить, что еще Xcode создал для нас при настройке проекта.

В Навигаторе проекта (Project Navigator), слева, вы должны видеть файл с именем Core_Data.xcdatamodeld. Это модель данных приложения, которая компилируется в .momd-файл. Это именно тот .momd-файл, который использует управляемую объектную модель для создания модели данных приложения.

 

Это позволяет иметь несколько файлов модели данных. Класс NSManagedObjectModel вполне способен объединить множество моделей данных в одну, что является одной из наиболее мощных и продвинутых функций Core Data.

Также Core Data поддерживает версионирование модели данных, в том числе при миграции. Это гарантирует, что данные, хранящиеся в постоянном хранилище(-ах) останутся целыми. Версионирование и миграция будут рассмотрены в следующих статьях этой серии.

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

 

6. Собирая все вместе

Прежде чем мы закончим эту статью, мы покажем вам схему, которая иллюстрирует три компонента стека Core Data.

Схема, представленная выше — это визуальное представление того, что мы только что изучали на примере проекта. Объект NSPersistentStoreCoordinator — это мозг стека Core Data в приложении. Он общается с одним или более постоянных хранилищ и гарантирует сохранение, загрузку и кэширование данных.

Координатор постоянного хранилища знает о модели данных (схеме графа объектов, если вам так нравится) через объект NSManagedObjectModel. Управляемая объектная модель создает модель данных приложения из одного или нескольких .momd-файлов, двоичного (бинарного) представления модели данных.

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

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

Заключение

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

В следующей статье этой серии мы более подробно поговорим о моделе данных. Мы рассмотрим редактор модели данных в Xcode и создадим несколько сущностей, атрибутов и взаимосвязей.

Пролистать наверх