Вандад Нахавандипур - iOS. Приемы программирования
Рис. 16.11. Пустой табличный вид, построенный на базе контроллера для представления результатов выборки
Переходим ко второму контроллеру вида, где пользователь может добавить новый экземпляр Person в контекст управляемых объектов. Воспользуемся следующим методом:
— (void) createNewPerson:(id)paramSender{
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
NSManagedObjectContext *managedObjectContext =
appDelegate.managedObjectContext;
Person *newPerson =
[NSEntityDescription insertNewObjectForEntityForName:@"Person"
inManagedObjectContext: managedObjectContext];
if (newPerson!= nil){
newPerson.firstName = self.textFieldFirstName.text;
newPerson.lastName = self.textFieldLastName.text;
newPerson.age = @([self.textFieldAge.text integerValue]);
NSError *savingError = nil;
if ([managedObjectContext save:&savingError]){
[self.navigationController popViewControllerAnimated: YES];
} else {
NSLog(@"Failed to save the managed object context.");
}
} else {
NSLog(@"Failed to create the new person object.");
}
}
Этот метод считывает имя, фамилию и возраст человека. На основе этих трех информационных фрагментов в контроллере вида будет создаваться контакт. Нам не придется заниматься реализацией этих текстовых полей, поскольку такая работа никак не связана с темой данного раздела. После вызова метода мы вызываем в контексте управляемого объекта метод save:. Он, в свою очередь, инициирует изменения в контроллере вида для представления результатов выборки (он находится в табличном виде). В результате всего этого табличный вид обновится.
Наконец, мы должны предоставить пользователю возможность удалять элементы в контроллере первого (табличного) вида:
— (void) tableView:(UITableView *)tableView
commitEditingStyle:(UITableViewCellEditingStyle)editingStyle
forRowAtIndexPath:(NSIndexPath *)indexPath{
Person *personToDelete = [self.frc objectAtIndexPath: indexPath];
[[self managedObjectContext] deleteObject: personToDelete];
if ([personToDelete isDeleted]){
NSError *savingError = nil;
if ([[self managedObjectContext] save:&savingError]){
NSLog(@"Successfully deleted the object");
} else {
NSLog(@"Failed to save the context with error = %@", savingError);
}
}
}
Этот код даже не затрагивает непосредственно сам контроллер для представления результатов выборки, но удаляет выбранный контакт из контекста управляемых объектов. В результате обновляется содержимое контроллера, представляющего результаты выборки, а это, в свою очередь, приводит к обновлению табличного вида. Подробнее о табличных видах рассказано в главе 4. Интерфейс нашего контроллера табличного вида в режиме удаления может выглядеть, примерно как на рис. 16.12.
Рис. 16.12. Табличный контроллер вида в режиме удаления, также здесь используется контроллер вида для представления результатов выборки
16.8. Реализация отношений в Core Data
Постановка задачи
Необходимо иметь возможность связывать управляемые объекты друг с другом, например связать контакт Person с каталогом Home, в котором он находится.
Решение
Применяйте в редакторе модели обратные отношения.
Обсуждение
В Core Data могут существовать следующие виды отношений: «один к одному» (one-to-one), обратное отношение «один ко многим» или обратное отношение «многие ко многим». Далее приведены жизненные примеры каждой разновидности отношений.
• Отношение «один к одному» — существует между человеком и его носом. У каждого человека может быть только один нос, и каждый нос может принадлежать только одному человеку.
Обратное отношение «один ко многим» — существует между сотрудником и его менеджером. У сотрудника может быть только один непосредственный менеджер, но одному менеджеру могут одновременно подчиняться несколько сотрудников. В данном случае для сотрудника создается отношение «один к одному», однако для менеджера это отношение «один (менеджер) ко многим (сотрудникам)». Поэтому такое отношение и называется обратным.
Обратное отношение «многие ко многим» — возникает между человеком и автомобилем. Одна машина может использоваться несколькими людьми, а один человек может пользоваться несколькими машинами.
В Core Data можно создавать отношения «один к одному», но я категорически не рекомендую этого делать. Возвращаясь к недавнему примеру с носом, необходимо отметить, что человек будет знать, чей нос торчит у него на лице, а вот нос не будет знать, кому он принадлежит. Обратите внимание на то, что эта система отношений «один к одному» отличается от взаимно однозначных отношений, с которыми вы могли столкнуться в других системах управления базами данных: объект A и объект B будут взаимосвязаны друг с другом, если между ними существует отношение «один к одному». В Core Data при отношении «один к одному» объект A будет знать, что связан с объектом B, но не наоборот. В объектно-ориентированном языке, таком как Objective-C, всегда лучше создавать обратные отношения, такие, которые позволяют дочерним элементам обращаться к родительским. При отношении «один ко многим» объект, который может быть ассоциирован с рядом других объектов, будет удерживать это множество объектов. Это будет множество типа NSSet. Хотя при отношениях «один к одному» оба объекта, состоящие в таких отношениях, сохраняют ссылку друг на друга, так как используют правильное имя класса «напарника», это отношение все равно принадлежит к типу «один к одному», и один объект может быть представлен в другом путем простого указания своего имени класса.
Итак, создадим такую модель данных, в которой используются преимущества обратного отношения «один ко многим».
1. Найдите в Xcode файл xcdatamodel, созданный системой в самом начале работы с проектом Core Data. Это было показано во введении к данной главе (создание такого проекта описано в разделе 16.1).
2. Откройте в редакторе файл модели данных, щелкнув на нем кнопкой мыши.
3. Удалите все созданные ранее сущности, выделяя их и нажимая клавишу Delete.
4. Создайте новую сущность и назовите ее Employee (Сотрудник). Создайте для этой сущности три атрибута, которые будут называться firstName (типа String), lastName (типа String) и age (типа Integer 32) (рис. 16.13).
Рис. 16.13. Сущность Employee с тремя атрибутами
5. Создайте сущность под названием Manager (Менеджер) с такими же атрибутами, как и у сущности Employee: firstName (типа String), lastName (типа String) и age (типа Integer 32) (рис. 16.14).
Рис. 16.14. Сущность Manager с тремя атрибутами
6. Создайте новое отношение для сущности Manager. Для этого сначала нужно выбрать данную сущность из списка, а потом нажать кнопку + в нижней части области Relationships (Отношения) (рис. 16.15).
Рис. 16.15. Добавление нового отношения к сущности Manager
7. В качестве имени нового отношения задайте employees (Сотрудники) (рис. 16.16).
Рис. 16.16. Изменение имени нового отношения типа «менеджер к сотрудникам»
8. Выберите сущность Employee и создайте для нее новое отношение. Назовите это отношение manager (рис. 16.17).
Рис. 16.17. Изменение имени нового отношения между сотрудниками и менеджером
9. Выберите сущность Manager, а потом выделите отношение employees для Manager. В области Relationships (Отношения) выберите параметр Employee (Сотрудник) в раскрывающемся меню Destination (Назначение). Именно так — ведь в этом отношении мы хотим соединить сущности Manager и Employee. В столбце Inverse (Обратные отношения) укажите значение manager (так как отношение manager будет связывать сотрудника (Employee) с менеджером (Manager)). Наконец, установите флажок To-Many Relationship (Отношение ко многим) в инспекторе модели данных (см. раздел 16.1). Результаты приведены на рис. 16.18.
Рис. 16.18. Обратное отношение, установленное между менеджером и сотрудниками
10. Выделите обе сущности (Employee и Manager), выполните команду File — New File (Файл — Новый файл) и создайте классы управляемых объектов для вашей модели, как описано в разделе 16.2.
Создав обратное отношение «один ко многим», откройте. h-файл вашей сущности Employee:
#import <Foundation/Foundation.h>
#import <CoreData/CoreData.h>
@class Manager;
@interface Employee: NSManagedObject
@property (nonatomic, retain) NSNumber * age;
@property (nonatomic, retain) NSString * firstName;
@property (nonatomic, retain) NSString * lastName;