Электронная библиотека » Вандад Нахавандипур » » онлайн чтение - страница 50


  • Текст добавлен: 14 июля 2014, 12:45


Автор книги: Вандад Нахавандипур


Жанр: Зарубежная компьютерная литература, Зарубежная литература


сообщить о неприемлемом содержимом

Текущая страница: 50 (всего у книги 59 страниц)

Шрифт:
- 100% +
Обсуждение

Тем, кто знаком с терминологией баз данных, запрос выборки данных напомнит оператор SELECT. В операторе SELECT мы указываем, какие строки и при каких условиях должны быть возвращены из какой таблицы. Запрос выборки данных делает то же самое. Мы указываем сущность (таблицу) и контекст управляемых объектов (уровень базы данных). Кроме того, можем задавать дескрипторы сортировки для данных, которые мы считываем. Но сначала поговорим о том, как упростить сам процесс считывания данных.

Чтобы считать сущность Person (эту сущность мы создали в разделе 16.1 и превратили ее в код в разделе 16.2), мы сначала приказываем классу NSEntityDescription произвести в нашем контексте управляемых объектов поиск сущности под названием Person. Как только она будет найдена, сообщим запросу выборки данных, что требуется считать информацию из этой сущности. После этого нам останется всего лишь выполнить запрос выборки данных, как было показано в подразделе «Решение» данного раздела.

Метод экземпляра executeFetchRequest: error:, относящийся к классу NSManagedObjectContext, может иметь в качестве возвращаемого значения либо nil (в случае ошибки), либо массив управляемых объектов Person. Если по заданной сущности не найдено никаких результатов, то возвращенный массив будет пустым.

См. также

Разделы 16.1 и 16.2.

16.5. Удаление данных из Core Data
Постановка задачи

Требуется удалить управляемый объект (строку таблицы) из контекста управляемых объектов (вашей базы данных).

Решение

Воспользуйтесь методом экземпляра deleteObject:, относящимся к классу NSManagedObjectContext:


– (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{


[self createNewPersonWithFirstName:@"Anthony"

lastName:@"Robbins"

age:51];


[self createNewPersonWithFirstName:@"Richard"

lastName:@"Branson"

age:61];


/* Сначала создаем запрос выборки данных. */

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];


NSError *requestError = nil;


/* Теперь применим запрос выборки данных к контексту. */

NSArray *persons =

[self.managedObjectContext executeFetchRequest: fetchRequest

error:&requestError];


/* Убеждаемся, что получили массив. */

if ([persons count] > 0){


/* Удаляем последний контакт из массива. */

Person *lastPerson = [persons lastObject];


[self.managedObjectContext deleteObject: lastPerson];


NSError *savingError = nil;

if ([self.managedObjectContext save:&savingError]){

NSLog(@"Successfully deleted the last person in the array.");

} else {

NSLog(@"Failed to delete the last person in the array.");

}


} else {

NSLog(@"Could not find any Person entities in the context.");

}


self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];


self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

В приведенном примере кода используется метод createNewPersonWithFirstName: lastName: age:, который мы написали в разделе 16.4.

Обсуждение

Можно удалять управляемые объекты (записи из таблицы базы данных) с помощью метода экземпляра deleteObject:, относящегося к классу NSManagedObjectContext.

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

С учетом данной информации изменим код, написанный ранее в этом разделе:


– (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{


[self createNewPersonWithFirstName:@"Anthony"

lastName:@"Robbins"

age:51];


[self createNewPersonWithFirstName:@"Richard"

lastName:@"Branson"

age:61];


/* Сначала создаем запрос выборки данных. */

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc] init];


NSError *requestError = nil;


/* Теперь применим запрос выборки данных к контексту. */

NSArray *persons =

[self.managedObjectContext executeFetchRequest: fetchRequest

error:&requestError];


/* Убеждаемся, что получили массив. */

if ([persons count] > 0){


/* Удаляем последний контакт из массива. */

Person *lastPerson = [persons lastObject];


[self.managedObjectContext deleteObject: lastPerson];


if ([lastPerson isDeleted]){

NSLog(@"Successfully deleted the last person…");


NSError *savingError = nil;

if ([self.managedObjectContext save:&savingError]){

NSLog(@"Successfully saved the context.");

} else {

NSLog(@"Failed to save the context.");

}


} else {

NSLog(@"Failed to delete the last person.");

}


} else {

NSLog(@"Could not find any Person entities in the context.");

}


self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];


self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

После запуска приложения в окне консоли отобразится примерно следующий результат:

Successfully deleted the last person… // последний контакт успешно удален

Successfully saved the context. // контекст успешно сохранен

16.6. Сортировка данных в Core Data
Постановка задачи

Требуется сортировать управляемые объекты (записи), выбираемые из контекста управляемых объектов (базы данных).

Решение

Нужно создать по экземпляру класса NSSortDescriptor для каждого атрибута (в терминологии баз данных – столбца) той сущности, в которой требуется произвести сортировку. Дескрипторы сортировки добавляются к массиву, а сам массив присваивается экземпляру NSFetchRequest с помощью метода экземпляра setSortDescriptors:. В данном коде, приведенном в качестве примера, Sorting_Data_in_Core_DataAppDelegate – это класс, представляющий делегат универсального приложения (о том, как создается сущность Person, рассказано в разделах 16.1 и 16.2:


– (BOOL) application:(UIApplication *)application

didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{


[self createNewPersonWithFirstName:@"Richard"

lastName:@"Branson"

age:61];


[self createNewPersonWithFirstName:@"Anthony"

lastName:@"Robbins"

age:51];


/* Сначала создаем запрос выборки данных. */

NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]

initWithEntityName:@"Person"];


NSSortDescriptor *ageSort =

[[NSSortDescriptor alloc] initWithKey:@"age"

ascending: YES];


NSSortDescriptor *firstNameSort =

[[NSSortDescriptor alloc] initWithKey:@"firstName"

ascending: YES];

fetchRequest.sortDescriptors = sortDescriptors;


/* Сообщаем запросу, что сначала мы хотим

считать содержимое сущности Person. */

[fetchRequest setEntity: entity];


NSError *requestError = nil;


/* Теперь применим запрос выборки данных к контексту. */

NSArray *persons =

[self.managedObjectContext executeFetchRequest: fetchRequest

error:&requestError];


for (Person *person in persons){


NSLog(@"First Name = %@", person.firstName);

NSLog(@"Last Name = %@", person.lastName);

NSLog(@"Age = %lu", (unsigned long)[person.age unsignedIntegerValue]);


}


self.window = [[UIWindow alloc] initWithFrame:

[[UIScreen mainScreen] bounds]];


self.window.backgroundColor = [UIColor whiteColor];

[self.window makeKeyAndVisible];

return YES;

}

Обсуждение

Экземпляр класса NSFetchRequest может нести с собой массив экземпляров NSSortDescriptor. Каждый дескриптор сортировки определяет атрибут (столбец) актуальной сущности, в которой необходимо произвести сортировку. Кроме того, он указывает порядок сортировки – восходящий или нисходящий. Например, сущность Person, которую мы создали в разделе 16.1, имеет атрибуты firstName, lastName и age. Если мы хотим считать все контакты в контексте управляемых объектов и отсортировать этих людей по возрасту, от самого младшего до самого старшего, то создадим экземпляр NSSortDescriptor с ключом age и зададим для него восходящий порядок (ascending):


NSSortDescriptor *ageSortDescriptor =

[[NSSortDescriptor alloc] initWithKey:@"age"

ascending: YES];

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

См. также

Раздел 16.4.

16.7. Оптимизация доступа к данным в табличных видах
Постановка задачи

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

Решение

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

В этом разделе для ускорения разработки рассматриваемого приложения будут применены раскадровки. Подробнее о раскадровках рассказано в главе 6.

Обсуждение

Контроллер для представления результатов выборки (Fetched Result Controller) функционально аналогичен табличному виду. Как и в таблице, в нем есть разделы и строки. Контроллер для представления результатов выборки может считывать управляемые объекты из соответствующего контекста, а также подразделять эти объекты на разделы и строки. Каждый раздел является группой, если задать такое условие в параметрах запроса, а каждая строка в разделе является управляемым объектом. Есть несколько важных причин, по которым может понадобиться модифицировать ваше приложение, чтобы в нем можно было применять контроллеры для представления результатов выборки. Эти причины таковы.

• После создания контроллера для представления результатов выборки в контексте управляемых объектов любое изменение данных (вставка, удаление, модификация и т. д.) немедленно отразится и в контроллере для представления результатов выборки. Например, можно создать контроллер для представления результатов выборки, чтобы считывать управляемые объекты сущности Person. Потом где-то в другой точке вашего приложения может понадобиться вставить в контекст новый управляемый объект Person (речь идет о том самом контексте, в котором был создан контроллер для представления результатов выборки). Сразу же после этого новый управляемый объект станет доступен в контроллере для представления результатов выборки. Чудеса, да и только!

• Имея контроллер для представления результатов выборки, можно более эффективно управлять кэшем. Например, можно указать контроллеру для представления результатов выборки сохранить в памяти только N управляемых объектов на каждый экземпляр такого контроллера.

• Контроллеры для представления результатов выборки аналогичны табличным видам в том отношении, что в них, как и в таблицах, есть разделы и строки – об этом говорилось ранее. Можно использовать контроллер для представления результатов выборки, чтобы без труда отображать табличные виды вашего приложения прямо в графическом пользовательском интерфейсе.


Рассмотрим некоторые важные свойства и методы экземпляра, относящиеся к контроллерам для представления результатов выборки (все объекты относятся к типу NSFetchedResultsController).

• sections (свойство типа NSArray) – контроллер для представления результатов выборки может группировать данные, используя путь к ключу. Выделенный инициализатор класса NSFetchedResultsController принимает данный группирующий фильтр в параметре sectionNameKeyPath. После этого в массиве sections будут содержаться все сгруппированные разделы. Каждый объект данного массива соответствует протоколу NSFetchedResultsSectionInfo.

• objectAtIndexPath: (метод экземпляра, возвращает управляемый объект) – объекты, выбираемые с помощью описываемого контроллера, их можно получать по индексу в разделе или строке. Строки каждого раздела нумеруются от 0 до N – 1, где N – общее количество элементов в данном разделе. В объекте пути к индексу указывается как индекс раздела, так и индекс строки, и в результате этого совершенно точно формулируется информация, необходимая для получения конкретных объектов от контроллера для представления результатов выборки. Метод экземпляра objectAtIndexPath принимает индексные пути. Каждый индексный путь – это объект типа NSIndexPath. Если требуется создать ячейку табличного вида, воспользовавшись управляемым объектом из контроллера для представления результатов выборки, то нужно просто передать объект индексного пути методу делегата табличного вида tableView: cellForRowAtIndexPath:. Такая передача происходит в параметре cellForRowAtIndexPath этого метода. Если вы хотите сами создать индексный путь в любой другой точке вашего приложения, пользуйтесь методом класса indexPathForRow: inSection:, относящимся к классу NSIndexPath.

• fetchRequest (свойство типа NSFetchRequest) – если в любой точке вашего приложения возникнет необходимость заменить объект запроса выборки контроллером для представления результатов выборки, то это можно сделать с помощью свойства fetchRequest экземпляра NSFetchedResultsController. Такая возможность будет полезна, например, если необходимо изменить дескрипторы сортировки (о них подробно рассказано в разделе 16.6) запроса выборки – уже после того, как вы выделили и инициализировали ваши контроллеры для представления результатов выборки.


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

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


#import «PersonsListTableViewController.h»

#import «AppDelegate.h»

#import «Person.h»

#import «AddPersonViewController.h»


static NSString *PersonTableViewCell = @"PersonTableViewCell";


@interface PersonsListTableViewController ()

<NSFetchedResultsControllerDelegate>


@property (nonatomic, strong) UIBarButtonItem *barButtonAddPerson;

@property (nonatomic, strong) NSFetchedResultsController *frc;


@end


Кнопка панели, объявленная в этом коде, будет представлять собой простую кнопку +. Этот плюсик будет находиться на навигационной панели. Такая кнопка позволяет пользователю перейти в контроллер вида Add Person (Добавить контакт), где можно будет добавить новый контакт в имеющийся контекст управляемых объектов. Контроллер для представления результатов выборки также будет использоваться для выборки контактов из контекста и последующего их отображения в табличном виде.

Вот как создается контроллер для представления результатов выборки:


/* Сначала создаем запрос выборки данных */


NSFetchRequest *fetchRequest = [[NSFetchRequest alloc]

initWithEntityName:@"Person"];


NSSortDescriptor *ageSort =

[[NSSortDescriptor alloc] initWithKey:@"age"

ascending: YES];


NSSortDescriptor *firstNameSort =

[[NSSortDescriptor alloc] initWithKey:@"firstName"

ascending: YES];


fetchRequest.sortDescriptors = @[ageSort, firstNameSort];


self.frc =

[[NSFetchedResultsController alloc]

initWithFetchRequest: fetchRequest

managedObjectContext: [self managedObjectContext]

sectionNameKeyPath: nil

cacheName: nil];


self.frc.delegate = self;

NSError *fetchingError = nil;

if ([self.frc performFetch:&fetchingError]){

NSLog(@"Successfully fetched.");

} else {

NSLog(@"Failed to fetch.");

}


Как видите, контроллер для представления результатов выборки принимает контроллер актуального табличного вида в качестве своего делегата. Делегат контроллера для представления результатов выборки должен соответствовать протоколу NSFetchedResultsControllerDelegate. Вот некоторые из наиболее важных методов этого протокола.

• controllerWillChangeContent: – вызывается в делегате и сообщает ему об изменении контекста, служащего основой для контроллера, представляющего результаты выборки, а также о том, что содержимое контроллера, представляющего результаты выборки, вот-вот изменится с учетом внесенных изменений. Обычно этот метод используется для подготовки табличного вида к изменениям. Для этого в нем вызывается метод beginUpdates.

• controller: didChangeObject: atIndexPath: forChangeType: newIndexPath: – вызывается в делегате и сообщает ему о конкретных изменениях, сделанных в объекте из контекста. Например, если вы удаляете объект в контексте, то вызывается этот метод. При этом его параметр forChangeType содержит значение NSFetchedResultsChangeDelete. В другом случае, когда вы вставляете новый объект в контекст, этот параметр содержит значение NSFetchedResultsChangeInsert.


Кроме того, этот метод вызывается в методе делегата контроллера для представления результатов выборки, когда обновляется управляемый объект. Это происходит после того, как объект будет сохранен в объекте с помощью метода save:.

• controllerDidChangeContent: – вызывается в делегате и информирует его о том, что контроллер для представления результатов выборки был обновлен в результате обновления контекста управляемых объектов. Как правило, именно внутри этого метода программисты совершают вызов endUpdates, применяемый в табличных видах для обработки всех обновлений, поступивших в таблицу после срабатывания метода beginUpdates.

Вот типичная реализация вышеупомянутых методов в приложении, которое было описано ранее в этом разделе:


– (void) controllerWillChangeContent:(NSFetchedResultsController *)controller{

[self.tableView beginUpdates];

}


– (void) controller:(NSFetchedResultsController *)controller

didChangeObject:(id)anObject

atIndexPath:(NSIndexPath *)indexPath

forChangeType:(NSFetchedResultsChangeType)type

newIndexPath:(NSIndexPath *)newIndexPath{


if (type == NSFetchedResultsChangeDelete){

[self.tableView

deleteRowsAtIndexPaths:@[indexPath]

withRowAnimation: UITableViewRowAnimationAutomatic];

}


else if (type == NSFetchedResultsChangeInsert){

[self.tableView

insertRowsAtIndexPaths:@[newIndexPath]

withRowAnimation: UITableViewRowAnimationAutomatic];

}


}


– (void) controllerDidChangeContent:(NSFetchedResultsController *)controller{

[self.tableView endUpdates];

}


Остановимся также на передаче информации в табличный вид с помощью различных методов контроллера для представления результатов выборки – об этом мы также упоминали ранее. Одним из таких методов является objectAtIndexPath:. Простая реализация этого метода в табличном виде может выглядеть примерно так:


– (NSInteger)tableView:(UITableView *)tableView

numberOfRowsInSection:(NSInteger)section{


id <NSFetchedResultsSectionInfo> sectionInfo =

self.frc.sections[section];

return sectionInfo.numberOfObjects;

}


– (UITableViewCell *)tableView:(UITableView *)tableView

cellForRowAtIndexPath:(NSIndexPath *)indexPath{


UITableViewCell *cell = nil;


cell = [tableView dequeueReusableCellWithIdentifier: PersonTableViewCell

forIndexPath: indexPath];


Person *person = [self.frc objectAtIndexPath: indexPath];


cell.textLabel.text =

[person.firstName stringByAppendingFormat:@" %@", person.lastName];


cell.detailTextLabel.text =

[NSString stringWithFormat:@"Age: %lu",

(unsigned long)[person.age unsignedIntegerValue]];


return cell;

}


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


Рис. 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. Табличный контроллер вида в режиме удаления, также здесь используется контроллер вида для представления результатов выборки


Страницы книги >> Предыдущая | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | Следующая
  • 0 Оценок: 0

Правообладателям!

Это произведение, предположительно, находится в статусе 'public domain'. Если это не так и размещение материала нарушает чьи-либо права, то сообщите нам об этом.


Популярные книги за неделю


Рекомендации