0,0
рейтинг
26 августа 2013 в 02:23

Разработка → Core Data для iOS. Глава №1. Теоретическая часть

Хабралюди, добрый день!
Сегодня хочу начать написание ряда лекций с практическими заданиями по книги Михаеля Привата и Роберта Варнера «Pro Core Data for iOS», которую можете купить по этой ссылке. Каждая глава будет содержать теоретическую и практическую часть.



Содержание:
  • Глава №1. Приступаем (Практическая часть)
  • Глава №2. Усваиваем Core Data
  • Глава №3. Хранение данных: SQLite и другие варианты
  • Глава №4. Создание модели данных
  • Глава №5. Работаем с объектами данных
  • Глава №6. Обработка результатирующих множеств
  • Глава №7. Настройка производительности и используемой памяти
  • Глава №8. Управление версиями и миграции
  • Глава №9. Управление таблицами с использованием NSFetchedResultsController
  • Глава №10. Использование Core Data в продвинутых приложениях



Приступаем


Что такое Core Data?

Используя компьютеры для выполнения своих задач, люди рассчитывают, что внесенные ими изменения будет сохранены. Сохранение изменений играет важную роль в офисных программных пакетах, текстовых редакторах, играх, браузерах и тд. Большинство программного обеспечения нуждается в возможности хранить введенные пользователем данные для последующего восстановления состояния работы, но конечно же есть и такое ПО, которое в этом не нуждается — калькуляторы, новостные ленты, будильники, виджеты о погоде.
Понимание того, каким образом можно хранить данные на iDevice, является критически важным при разработке продвинутых приложений.

Apple предоставляет гибкий фрэймворк для работы с хранимыми на устройстве данными — Core Data. Конечно же Core Data не панацея и есть другие варианты хранения данных, которые могут лучше подойти при решении определенных задач, но уж очень хорошо и красиво Core Data вписывается в Cocoa Touch. Большинство деталей по работе с хранилищем данных Core Data скрывает, позволяя вам сконцентрироваться на том, что действительно делает ваше приложение веселым, уникальным и удобным в использовании.

Не смотря на то, что Core Data может хранить данные в реляционной базе данных вроде SQLite, Core Data не является СУБД. По-правде Core Data в качестве хранилища может вообще не использовать реляционные базы данных. Core Data не является чем-то вроде Hibernate, хотя и предоставляет некоторые возможности ORM. Core Data скорее является оболочкой/фрэймворком для работы с данными, которая позволяет работать с сущностями и их связями (отношениями к другим объектами), атрибутами, в том виде, который напоминает работы с объектным графом в обычном объектно-ориентированном программировании.

Знаете ли Вы?
Core Data был внедрён лишь начиная с Mac OS X 10.4 Tiger и iPhone SDK 3.0


История хранения данных в iOS

Как за выпущенные Pixar мультфильмы мы должны благодарить компанию NeXT, так и за Core Data мы должны сказать ей спасибо. Core Data родилась и эволюционировала из технологии, называемой Enterprise Objects Framework (EOF).
Дебют фрэймворка приходится на 2005 год с запуском Mac OS X 10.4 (Tiger), а вот в IPhone появляется лишь начиная с версии 3.0.
До того, как Core Data пришла на IPhone, разработчикам приходилось не сладко и для хранения данных могли быть использованы следующие варианты:
1) Списки свойств, которые содержали пары ключ-значение из различных типов данных.
2) Сериализация данных и хранение их в файлах (использовался протокол NSCoding)
3) Реляционная база данных SQLite
4) Хранение данных в облаке

Эти методы всё еще продолжают использоваться, хотя и ни один метод из четырёх не сравнится по удобству с использованием Core Data. Несмотря на рождение таких фрэймворком, как FMDatabase и ActiveRecord, для решения проблемы с хранением данных до появления Core Data, разработчики с удовольствием переключились на Core Data после его появления.
Хотя повторюсь, что Core Data не является лекарством от всех болезней, иногда вы конечно будете обращаться к решениям с использованием, например, списка свойств, но чаще всего вы будете сталкиваться с необходимостью и желанием использовать в своих приложениях именно Core Data, как инструмент, который наилучшим образом позволяет решить вашу проблему.
Чем чаще и чем больше вы будете программировать и использовать Core Data, тем чаще у вас будет возникать не вопрос «Стоит ли мне использовать Core Data?», а «Есть ли причина не использовать Core Data?».

Создание простого приложения с Core Data

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

Понимание основных компонентов Core Data

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

Как пользователь Core Data вы никогда не должны работать напрямую с хранилищем данных. Абстрагируйтесь от хранилища, от типа хранилища, думайте только о данных! Особенностью такого подхода является возможность безболезненно сменить тип хранилища (был XML файл, а стал SQLite) не меняя большое кол-ва написанного вами кода.
Объекты, которые находятся под управлением фрэймворка (Core Data) должны наследовать методы/свойства класса NSManagedObject.
Так же, как и людям, объектам нужна среда в которой они могут существовать, такая среда есть и, называется она managed object context (среда управляемых объектов) или просто context. Среда, в которой находится объект, следит не только за тем, в каком состоянии находится объект с которым вы работаете, но и за состояниями связанных объектов (объектов, которые зависимы от данного и от которых зависим он сам).
Экземпляр класса NSManagedObjectContext предоставляет ту самую среду для объектов, объект данного типа должен быть доступен в вашем приложении всегда. Обычно экземпляр класса NSManagedObjectContext является свойством делегата вашего приложения. Без среды, без экземпляра класса NSManagedObjectContext вам просто не удастся работать с Core Data.

Создание нового проекта

Запустим XCode и создадим новый проект из шаблона Master-Detail Application:
image

Заполним поля следующим образом:
image

И после завершения создания увидим примерно такую картину:
image

Запускаем наш проект

Перед тем как начать разбираться, что находится под капотом данного приложения, давайте запустим и разберемся, что вообще делает приложение.
Жмём на кнопку «Run» и перед нами появится вот такое:
image

Нажмем несколько раз на кнопку с "+" и в списке появится несколько записей со временем.
image

Теперь завершим (остановим) работу приложения и, если приложение не использовало бы Core Data для хранения данных, то при очередном запуске мы увидели бы пустой список снова, однако после перезапуска мы видим всё ту же картину:
image

Разбираем составляющие приложения

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

Обратите внимание на то, что класс MasterViewController содержит свойство, которое ссылается на экземпляр класса NSManagedObjectContext для взаимодействия с Core Data. Пройдясь по коду можно увидеть, что MasterViewController получает managed object context из свойства делегата приложения.

- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
   self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
   if(self){
      self.title = NSLocalizedString(@"Master", @"Master");
      id delegate = [[UIApplication sharedApplication] delegate];
      self.managedObjectContext = [delegate managedObjectContext];
   }
   
   return self;
}


BasicApplication.xcdatamodel представляет собой директорию в файловой системе, которая содержит информацию о структуре модели данных. Модель данных является основой каждого приложения использующего Core Data.
Модель данных данного приложения описывает лишь одну сущность — Event. Сущность Event содержит лишь одно свойство (поле, атрибут) — timeStamp типа Date.


Сущность Event типа NSManagedObject, который считается основным для всех сущностей находящимся под управлением Core Data. Во второй главе мы рассмотрим тип NSManagedObject более подробно.

Извлечение/выборка данных

Следующим классом, который нас интересует, является MasterViewController. В его заголовочном файле описаны два свойства на которые мы обратим внимание:
@property (nonatomic, strong) NSFetchedResultsController *fetchedResultsController;
@property (nonatomic, strong) NSManagedObjectContext *managedObjectContext;

NSFetchedResultsController представляет собой контроллер, предоставляемый фрэймворком Core Data для управления запросами к хранилищу.
NSManagedObjectContext является известной нам уже средой существования объектов типа NSManagedObject.

Реализация класса MasterViewController, которую можно найти в файле MasterViewController.m, показывает, каким образом можно взаимодействовать с Core Data для получения и хранения данных. В реализации класса MasterVIewController имеется явный геттер fetchedResultsController, который производит предварительную настройку запроса на выборку данных из хранилища.

Первым шагом к осуществлению запроса на выборку данных является создание запроса:
NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
[fetchRequest setEntity:entity];

Результаты запроса могут быть отсортированы при помощи NSSortDescriptor. NSSortDescriptor определяет поле для сортировки и тип сортировки (по возрастанию или убыванию).
В нашем примере сортируем по убыванию времена создания записей:
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
[fetchRequest setSortDescriptors:sortDescriptors];

После того, как запрос определен, мы можем приступить к созданию NSFetchedResultsController.
Используя в качестве делегата NSFetchedResultsController MasterVIewController мы можем следить за состоянием данных хранилища (удаление, добавление, перемещение и тд) и безболезненно интегрировать данное решение с UITableView. Мы можем конечно же получить те же результаты вызывая метод executeFetchRequest в managed object context, но в таком случае мы не получим и не сможем воспользоваться всеми преимуществами NSFetchedResultsController.
Создание и настройка переменной экземпляра класса NSFetchedResultsController:
NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
aFetchedResultsController.delegate = self;
self.fetchedResultsController = aFetchedResultsController;


Знаете ли Вы?
Вы наверно заметили, что используемый ранее метод initWithFetchRequest имеет параметр cacheName. При передаче в качестве аргумента nil Вы исключаете возможность кэширования результата запроса, но при указании наименования кэша, вы позволяете Core Data проверить наличие такого же ранее осуществленного запроса и вернуть результат из кэша. В противном случае, если запроса с таким именем кэша нет, то будет осуществлен запрос к хранилищу и возвращены необходимые данные, которые будет закэшированы впоследствии.


В завершение нам осталось только выполнить запрос для получения данных:
NSError *error = nil;
if(![self.fetchedResultsController performFetch:&error]){
   NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
   abort();
}

Ниже вы можете ознакомиться с полным геттером fetchedResultsController:
- (NSFetchedResultsController *)fetchedResultsController
{
    if (_fetchedResultsController != nil)
        return _fetchedResultsController;

   NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];
   NSEntityDescription *entity = [NSEntityDescription entityForName:@"Event" inManagedObjectContext:self.managedObjectContext];
   [fetchRequest setEntity:entity];


   [fetchRequest setFetchBatchSize:20];


   NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:@"timeStamp" ascending:NO];
   NSArray *sortDescriptors = [[NSArray alloc] initWithObjects:sortDescriptor, nil];
   [fetchRequest setSortDescriptors:sortDescriptors];


   NSFetchedResultsController *aFetchedResultsController = [[NSFetchedResultsController alloc] initWithFetchRequest:fetchRequest managedObjectContext:self.managedObjectContext sectionNameKeyPath:nil cacheName:@"Master"];
   aFetchedResultsController.delegate = self;
   self.fetchedResultsController = aFetchedResultsController;


   NSError *error = nil;
   if(![self.fetchedResultsController performFetch:&error]){
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
      abort();
   }


   return __fetchedResultsController;
}

NSFetchedResultsController представляет собой нечто вроде коллекции объектов типа NSManagedObject, для этого у него даже имеется свойства fetchedObjects типа NSArray для получения доступа к результатам запроса.
Класс MasterVIewController, который так же расширяет возможности UITableViewController, демонстрирует нам насколько удобно использовать NSFetchedResultsController для управления содержимым таблиц.

Вставка нового объекта

Быстро окинув взглядом метод insertNewObject: станет понятно, каким образом создаются и добавляются новые события в хранилище. NSManagedObject определяются описанием сущности из модели данных и могут существовать только в определенном контексте (среде). Первым шагом для создания нового объекта является получение контекста в котором этот объект будет создан:
NSManagedObjectContext *managedObjectContext = [self.fetchedResultsController managedObjectContext];
NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];

Создаём NSManagedObject:
NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntity:[entity name] inManagedObjectContext:context];
[newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];

Последним шагом, который необходимо осуществить, является сохранение контекста в котором был создан новый объект. Учтите, что при сохранении контекста все несохраненные ранее изменения будут сохранены.
NSError *error = nil;
if(![context save:&error]){
   NSLog(@"Unresolved error: %@, %@", error, [error userInfo]);
   abort();
}

Полный метод добавления нового объекта представлен ниже:
- (void)insertNewObject {
   NSManagedObjectContext *managedObjectContext = [self.fetchedResultsController managedObjectContext];
   NSEntityDescription *entity = [[self.fetchedResultsController fetchRequest] entity];
   NSManagedObject *newManagedObject = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];

   [newManagedObject setValue:[NSDate date] forKey:@"timeStamp"];

   NSError *error = nil;
   if(![context save:&error]){
      NSLog(@"Unresolved error: %@, %@", error, [error userInfo]);
      abort();
   }
}


Инициализация контекста (среды)

Очевидно, что всё, что мы раньше делали не может быть достигнуто без создания объекта контекста, без той самой среды в которой существует и живет объект. Как раз за создание этой самой среды отвечает делегат приложения. Три свойства, которые должны быть обязательно в любом приложении использующем Core Data:
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

Обратите внимание на то, что все три свойства readonly, делается это для того, чтобы извне их нельзя было установить. Изучая BasicApplicationAppDelegate.m видно, что все три свойства имеют явные геттеры.

Managed Object Model:
- (NSManagedObjectModel *)managedObjectModel {
   if(_managedObjectModel != nil){
      return _managedObjectModel;
   }

   NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"BasicApplication" withExtension:@"momd"];
   _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modeURL];

   return _managedObjectModel;
}

После чего создается хранилище поддерживающее созданные модели данных. В нашем случае, собственно, как и в большинстве других с использованием Core Data, хранилище данных основывается на SQLite.
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
   if(_persistentStoreCoordinator != nil){
      return _persistentStoreCoordinator;
   }

   NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"BasicApplication.sqlite"];

   NSError* error = nil;
   _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
   if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]){
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
      abort();
   }

   return _persistentStoreCoordinator;
}


Создаём контекст (среду):
- (NSManagedObjectContext *)managedObjectContext {
   if(_managedObjectContext != nil){
      return _managedObjectContext;
   }

   NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
   if(coordinator != nil){
      _managedObjectContext = [[NSManagedObjectContext alloc] init];
      [_managedObjectContext setPersistentStoreCoordinator:coordinator];
   }

   return _managedObjectContext;
}


Контекст используется во всём приложении в качестве интерфейса для взаимодействия с Core Data и постоянным хранилищем.
Последовательность инициализации Core Data:


Механизм запускается при вызове метода application:didFinishLaunchingWithOptions:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];

   MasterViewController *controller = [[MasterViewController alloc] initWithNibName:@"MasterViewController" bundle:nil];
   self.navigationController = [[UINavigationController alloc] initWithRootViewController:controller];
   self.window.rootViewController = self.navigationController;

   [self.window makeKeyAndVisible];
   return YES;
}


 Вызывая геттер свойства managedObjectContext делегата приложения на выполнение запускается цепочка действий:
— (NSManagedObjectContext *)managedObjectContext вызывает
— (NSPersistentStoreCoordinator *)persistentStoreCoordinator, который в свою очередь производит вызов
— (NSManagedObjectModel *)managedObjectModel.
Таким образом вызов геттер-метода managedObjectContext инициализирует весь стэк объектов Core Data и приводит Core Data в готовность.

Добавление Core Data в уже существующий проект

Осуществляется в три шага:
  1. Добавление фрэймворка Core Data
  2. Создание модели данных
  3. Инициализация контекста (среды)


Добавление фрэймворка Core Data

В мире Objective-C библиотеки называют фрэймворками.
Стандартные фрэймворки, которые уже подключены в «чистых» проектах и которые вы чаще всего увидите:
  • UIKit
  • Foundation
  • Core Graphics


Где производится добавление новых фрэймворков:


Выбираем фрэймворк для подключения:


Создание модели данных

Ни одно приложение Core Data не может считаться завершенным без модели данных. Модель данных описывает все сущности, которые будут находиться в ведении Core Data.
Для создания новой модели данных: File -> New -> New File


Назовем нашу модель MyModel.xcdatamodeld:


После создания модели перед вами откроется окно редактирования (создания) сущностей.


Создать новую сущность можно кликнув на кнопку "+" в левой нижней части XCode, а добавить новый атрибут сущности можно нажав на кнопку "+" уже в разделе «Attributes» и затем выбрать его тип.


Инициализация контекста (среды)

Последний шаг состоит в инициализации контекста, хранилища и объектной модели. Чаще всего вы можете воспользоваться кодом, который автоматически генерируется XCode в «пустых» проектах использующих Core Data.

DemoAppAppDelegate.h
#import <UIKit/UIKit.h>
#import <CoreData/CoreData.h>

@interface DemoAppDelegate : UIResponder <UIApplicationDelegate> {
}

@property (strong, nonatomic) UIWindow *window;
@property (strong, nonatomic) UINavigationController *navigationController;

@property (readonly, strong, nonatomic) NSManagedObjectModel *managedObjectModel;
@property (readonly, strong, nonatomic) NSManagedObjectContext *managedObjectContext;
@property (readonly, strong, nonatomic) NSPersistentStoreCoordinator *persistentStoreCoordinator;

- (void)saveContext;
- (NSURL *)applicationsDocumentsDirectory;

@end


Переключимся в *.m файл DemoAppAppDelegate и напишем следующие строки:
@synthesize managedObjectContext = _managedObjectContext;
@synthesize managedObjectModel = _managedObjectModel;
@synthesize persistentStoreCoordinator = _persistentStoreCoordinator;


Далее следует код, который производит инициализацию модели данных, хранилища и контекста.
- (NSManagedObjectModel *)managedObjectModel {
   if (_managedObjectModel != nil){
      return _managedObjectModel;
   }

   NSURL *modelURL = [[NSBundle mainBundle] URLForResource:@"MyModel" withExtension:@"momd"];
   _managedObjectModel = [[NSManagedObjectModel alloc] initWithContentsOfURL:modeURL];

   return _managedObjectModel;
}


- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {
   if(_persistentStoreCoordinator != nil){
      return _persistentStoreCoordinator;
   }

   NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:@"DemoApp.sqlite"];
   
   NSError *error = nil;
   _persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel: [self managedObjectModel]];
   if(![_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error&error]){
      NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
      abort();
   }

   return _persistentStoreCoordinator;
}


- (NSManagedObjectContext *)managedObjectContext {
   if(_managedObjectContext != nil){
      return _managedObjectContext;
   }

   NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
   if(coordinator != nil){
      _managedObjectContext = [[NSManagedObjectContext alloc] init];
      [_managedObjectContext setPersistentStoreCoordinator:coordinator];
   }

   return _managedObjectContext;
}


Методы, которые ранее мы еще нигде не реализовывали:
- (NSURL *)applicationDocumentsDirectory{
   return [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
}

- (void)saveContext {
   NSError *error = nil;
   NSManagedObjectContext *managedObjectContext = [self managedObjectContext];

   if(managedObjectContext != nil) {
      if([managedObjectContext hasChanges] && ![managedObjectContext save:&error]){
         NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
         abort();
      }
   }
}

Теперь, когда мы подключили Core Data, наше приложение может использовать его для хранения данных.
Давайте реализуем простой пример, который бы позволил нам убедиться в том, что всё работает так, как надо и данные действительно сохраняются.
В тестовом примере мы будем определять кол-во раз, которое было запущено наше приложение.
Внесём маленькие изменения в метод application:didFinishLaunchingWithOptions: делегата приложения в виде получения кол-ва раз, которое приложения запускалось ранее и добавление нового события запуска.

Код для получения предыдущих запусков приложения:
NSManagedObjectContext *context = [self managedObjectContext];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"MyData" inManagedObjectContext:context];
[request setEntity:entity];
NSArray *results = [context executeFetchRequest:request error:nil];


После чего мы можем пройтись по всему массиву следующим образом:
for(NSManagedObject *object in results){
   NSLog(@"Found %@", [object valueForKey:@"myAttribute"];
}

Добавим новую запись и сохраним:
NSString *launchTitle = [NSString stringWithFormat:@"launch %d", [results count]];
NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
[object setValue:launchTitle forKey:@"myAttribute"];
[self saveContext];
NSLog(@"Added : %@", launchTitle);

После первого запуска приложение выведет в консоль следующую строку:
2011–02-25 05:13:23.783 DemoApp[2299:207] Added: launch 0

После второго запуска:
2011–02-25 05:15:48.883 DemoApp[2372:207] Found launch 0 
2011–02-25 05:15:48.889 DemoApp[2372:207] Added: launch 1


Полный метод выглядит следующим образом:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
   NSManagedObjectContext *context = [self managedObjectContext];
   NSFetchRequest *request = [[NSFetchRequest alloc] init];
   NSEntityDescription *entity = [NSEntityDescription entytyForName:@"MyData" inManagedObjectContext:context];
   [request setEntity:entity];
   NSArray *results = [context executeFetchRequest:request error:nil];

   for(NSManagedObject *object in results){
      NSLog(@"Found %@", [object valueForKey:@"myAttribute"]);
   }

   NSString *launchTitle = [NSString stringWithFormat:@"launch %d", [results count]];
   NSManagedObject *object = [NSEntityDescription insertNewObjectForEntityForName:[entity name] inManagedObjectContext:context];
   [object setValue:launchTitle forKey:@"myAttribute"];
   [self saveContext];
   NSLog(@"Added : %@", launchTitle);   

   [self.window makeKeyAndVisible];
   return YES;
}


В завершение

Прошу об ошибках не писать в комментариях, лучше сразу в личные сообщения.

Продолжать ли переводы? Есть ли интерес к данной теме?

Благодарю за внимание!
Андрей Шмиг @AndrewShmig
карма
15,0
рейтинг 0,0
В поиске…
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (48)

  • +1
    Тема отличная. В рунете про Core Data написано очень и очень мало. Сам начинал читать эту книгу на английском, потом как-то забросил. Думаю, на русском будет куда проще воспринимать теоретическую информацию. Всячески поддерживаю перевод следующих разделов.
    • +3
      Благодарю за отзыв.
      Я по-правде хочу не просто переводить, но и добавлять нечто интересное вроде «Знаете ли Вы?» и практической части. Хотелось бы сделать такой большой и полезный туториал по Core Data.
  • +2
    Спасибо огромное за статьи! Вы делаете хорошую работу, переводя книгу!
    P.S. видел сей пост в песочнице, жаль не было инвайта.
    • +1
      Спасибо! Всегда приятно слышать, что твои труды кому-то нужны и работа проделана не в пустую.

      P.S. Был заблокирован модератором до 25, думал блок снимут 25 и опубликовал в этот день, но увы… пост провисел в песочнице весь день, а потом магическим образом исчез, хотя я думал он будет автоматически опубликован. Благо я сохранил исходный код статьи.
  • 0
    Спасибо Вам! Не знал, что есть книга отдельно о Core Data. Есть ли там информация о том, как работать с Core Data из разных потоков?
    • 0
      Почитайте документацию, там все предельно ясно написано. С выходом iOS 5.0 SDK стало чуть чуть полегче, но есть ньюансы с производительностью.
    • 0
      Ну как, контексты разные создавать для каждого потока, и обмениваться не объектами а ObjectID. Остальное уже за вами.
      • 0
        Так и делаю, только работает это все как-то мутно. Вдруг в сей книге дадут вошебный паттерн? :-)
        • 0
          А в core data вообще много всего работает мутно и неочевидно. Плата за универсальность. Я не фанат, но по долгу службы использовать приходилось, и довольно много.
        • 0
          А что именно вам кажется мутным?
          • 0
            Например object faulting вызывает зубную боль.
            • 0
              Почему? Мне кажется это вполне логичным. Если попробуете в консоль вывести NSManagedObject, внутри которого будет ссылка на самого себя, вы же не ожидаете от компилятора бесконченого цикла вывода текста в консоль?
              • 0
                Как вам такой глюк: при обращении к свойству объекта если он fault, получаем nil, а иногда не nil а то, что нужно. Происходит абсолютно рандомно. Потратил несколько ночей чтобы найти костыль.
                • +1
                  Хм, у меня такое было только когда были явные косяки с моей стороны.

                  Вы используете nested contexts stack, или два независимых контекста с общим persistent store coordinator?
                  • 0
                    Использовал два независимых контекста. Проблема была связана как раз с многопоточностью.
                    • 0
                      Ну если использовать оба контекста только через performBlock: и performBlockAndWait: (ну да, UI-контекст можно просто использовать в UI-потоке), проблем не должно быть. Можете поподробнее рассказать суть проблемы?
                      • +1
                        Приучаю себя пользовать GCD. :-)

                        Проблема такая (насколько помню по памяти, в исходнике искать долго):
                        Имеется такая иерархия Relationships где Conversations > Messages > User > Comments. То есть, крайняя сущность — это список комментариев пользователя. Приложение открывается по push-уведомлению и, если приложение было в фоне и получало didReceiveMemoryWarning, то при открытии окна с комментариями из профиля пользователя сразу после запуска приложения иногда возникал сплошной (null) (null) (null) вместо текста на контролах. Видимо, Core Data не успевал подгрузить свои внутренности.

                        Решил проблему так: использовал dispatch_async с задержкой 0.3 секунды (установлено эмпирическим путем). Вот такой некрасивый костыль.
                        • 0
                          Спасибо, буду готов к встрече с таким багом :)
                          • 0
                            Всегда пожалуйста! Опыт — сын ошибок трудных! :-)
                        • +1
                          Кстати, по поводу GCD у меня есть отличная книга, которую я всю перевел (а потом по глупости удалил с блогспота всё, может в кэше гугла что-то и осталось еще).
                          Если желаете, то могу после этой (Core Data) книги вновь перевести «Pro Multithreading...». А если хотите сами почитать — напишите в личку и я вышлю вам её в pdf формате.
                          image
                          • +1
                            Восхитительно!

                            Конечно желаю, да и хабрасообщество, думаю будет очень благодарным.

                            Кстати, я свободно владею английским, но все-равно русский вариант зачастую воспринимается гораздо лучше.
                          • 0
                            Ссылку в студию, или в личку.
                            Заранее огромное спасибо!!!
                          • 0
                            Только хотел в комментарии про неё написать. Было бы чудесно иметь перевод такой книги.
                        • 0
                          Лучше использовать не GCD, а NSOperation. По рекомендации эппла кстати.
                          • 0
                            Как же так? Ведь GCD — это стильно, модно, молодежно. :-)

                            NSOperation высокоуровневая обертка вокруг него. Тут уже дело вкуса на самом деле.
                            • +2
                              На самом деле, когда сталкиваешься с тем что асинхронные операции нужно иногда отменять, понимаешь почему эппл рекомендует использовать NSOperation.
                              • 0
                                Вот тут Вы правы! Действительно, при помощи GCD-шных функций это можно сделать только «ручками».
                              • 0
                                Именно это я и имел в виду!
                            • 0
                              In general, you should use the highest level of abstraction that suits your needs. This means that you should usually use NSOperationQueue instead of GCD, unless you need to do something that NSOperationQueue doesn't support.

                              Извините за английский, но думаю вы его знаете неплохо. Предполагаю, что это так потому что код более читаем, и блоками же можно добавлять операции, почти как GCD получается.
                          • 0
                            на самом деле GCD может много, чего не может NSOperation, а NSOperation умеет отмену и приоритеты. Так что все зависит от задачи, нельзя так однозначно говорить.
                            • 0
                              Они и не говорят однозначно, они говорят ровно следующее — используйте NSOperation там, где можно использовать NSOperation, в остальных случаях используйте GCD напрямую.

                              Ну примерно как используйте Core Data для работы с базой, используйте чистый SQLite только там где это необходимо.
                              • 0
                                Согласен, но я отвечал на вот этот Ваш комментарий:

                                Лучше использовать не GCD, а NSOperation. По рекомендации эппла кстати.


                                Пояснили Вы уже потом.
                                • 0
                                  Как на этот, когда он под тем, где я привел цитату из девелоперской документации.
                                  • 0
                                    А Вы стрелочку нажмите, ведущую к исходному комментарию.

                                    Да, ответил раньше, чем прочитал цитату из документации, каюсь.
            • 0
              Это же очень прозрачно работает. Наверное единственное когда вам придется об этом задуматься это при использовании transient properties, а так же в случаях когда столкнетесь с необходимостью prefetching. Советую посмотреть видео WWDC начиная с 2010 года, касательно core data.
              • 0
                Спасибо за наводку, посмотрю обязательно.
                • 0
                  Я уверен, что уважающий себя разработчик должен посмотреть их ВСЕ! :) Запасайтесь временем!
                  • 0
                    Вот времени как раз и не хватает… :-)
        • 0
          В списке много полезных книг и, все они со временем будут мною переведены.
          • 0
            image
            Сейчас ещё пытаюсь осилить эту книгу. В принципе, написано довольно понятно, но на русском было бы ещё лучше :)
    • 0
      И Вам спасибо за отзыв!
      Могу сказать одно — будет еще много чего и главное будет интересно (я постараюсь, чтобы так было), поэтому открыт любым пожеланиям по формату подачу информации, по каким-то рубрикам вроде «Знаете ли Вы?» (кстати решил сам добавить, не знаю насколько удалось и насколько вписывается сюда, интересно было бы услышать по этому поводу комментарии) и практическим заданиям, которые будут в ближайшее время.
    • +2
      Есть ещё одна отличная книга: Core Data: Core Data (2nd edition): Data Storage and Management for iOS, OS X, and iCloud от Marcus S. Zarra. Рекомендую.
      • +1
        Буквально сегодня утром на неё натыкался, в архивчик сохранил для последующего изучения.
      • +1
        Я был бы удивлен если бы книгу Marcus Zarra тут не упомянули — у меня сложилось ощущение что она самая рекомендуемая по Core Data.

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.