Обсуждение
Обсуждение
Задачи, связанные с пользовательским интерфейсом, должны выполняться в главном потоке. Поэтому единственным каналом для передачи в GCD задач, связанных с пользовательским интерфейсом, и их выполнения оказывается главная очередь. В качестве описателя главной диспетчерской очереди можно применять функцию dispatch_get_main_queue.
Существует два способа направления задач в основную очередь. Оба этих способа асинхронны и позволяют не прерывать исполнения программы на время, пока завершается операция:
функция dispatch_async выполняет блоковый объект применительно к диспетчерской очереди;
функция dispatch_async_f выполняет функцию C применительно к диспетчерской очереди.
Метод dispatch_sync нельзя применять к главной очереди, поскольку он заблокирует поток на неопределенное время и ваше приложение войдет во взаимную блокировку. Все задачи, направляемые в GCD через главную очередь, должны туда направляться асинхронно.
Рассмотрим использование функции dispatch_async, которая принимает два параметра:
описатель диспетчерской очереди — диспетчерская очередь, в которой должна выполняться задача;
блоковый объект — блоковый объект, посылаемый в диспетчерскую очередь для асинхронного выполнения.
Рассмотрим пример. В операционной системе iOS следующий код будет выводить пользователю предупреждение, и при этом будет применяться главная очередь:
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^(void) {
[[[UIAlertView alloc] initWithTitle:@"GCD"
message:@"GCD is amazing!"
delegate: nil
cancelButtonTitle:@"OK"
otherButtonTitles: nil, nil] show];
});
Как видите, функция GCD dispatch_async не имеет ни параметров, ни возвращаемого значения. Блоковый объект, передаваемый данной функции для выполнения задачи, должен самостоятельно собирать данные для этого. В только что рассмотренном примере кода присутствует вид с предупреждением, имеющий все значения, которые требуются ему для выполнения задачи. Но так бывает не всегда. Иногда вам необходимо удостовериться, что блоковый объект, передаваемый GCD, располагает в собственной области видимости всеми значениями, которые требуются для решения стоящей перед ним задачи.
Если запустить данное приложение в эмуляторе iOS, пользователь увидит примерно такую картинку, как на рис. 7.1.
Рис. 7.1. Предупреждение, при выводе которого применялись асинхронные вызовы к GCD.
В общем-то, этот результат не слишком впечатляет. Так благодаря чему же главная очередь так интересна? Ответ прост: когда приходится задействовать GCD на полную мощность, например, чтобы выполнить сложные вычисления в параллельных или последовательных потоках, вам, возможно, понадобится отображать текущие результаты для пользователя или перемещать какой-либо компонент на экране. В таких случаях необходимо применять основную очередь, так как это задачи, связанные с пользовательским интерфейсом. Функции, рассмотренные в этом разделе, дают единственную возможность выйти из параллельной или последовательной очереди для обновления пользовательского интерфейса, не прекращая работу с GCD. Можете себе представить, насколько они важны.
Если вы не хотите передавать блоковый объект для выполнения в главную очередь, можно передать объект, представляющий собой функцию на языке C. Передавайте функции dispatch_async_f все те функции C, которые предполагается направлять на выполнение в GCD и которые относятся к работе пользовательского интерфейса. Мы можем получить такие же результаты, как на рис. 7.1, используя вместо блоковых объектов функции на языке C, при этом потребуется внести в код лишь незначительные корректировки.
Как было указано ранее, функция dispatch_async_f позволяет передавать указатель на контекст, определяемый приложением. Этот контекст впоследствии может использоваться вызываемой нами функцией C. Итак, создадим структуру, в которой будут содержаться значения — в частности, заголовок предупреждающего вида, сообщение и надпись Cancel (Отмена) на соответствующей кнопке. Когда приложение запустится, мы поместим в эту структуру все значения и передадим ее функции C для отображения. Вот как определяется эта структура:
typedef struct{
char *title;
char *message;
char *cancelButtonTitle;
} AlertViewData;
Итак, продолжим и реализуем функцию C, которая в дальнейшем будет вызываться с GCD. Эта функция должна ожидать параметр типа void *, тип которого затем приводится к AlertViewData *. Иными словами, мы ожидаем от вызывающей стороны этой функции, что нам будет передана ссылка на данные, необходимые для работы предупреждающего вида, которые инкапсулированы в структуре AlertViewData:
void displayAlertView(void *paramContext){
AlertViewData *alertData = (AlertViewData *)paramContext;
NSString *title =
[NSString stringWithUTF8String: alertData->title];
NSString *message =
[NSString stringWithUTF8String: alertData->message];
NSString *cancelButtonTitle =
[NSString stringWithUTF8String: alertData->cancelButtonTitle];
[[[UIAlertView alloc] initWithTitle: title
message: message
delegate: nil
cancelButtonTitle: cancelButtonTitle
otherButtonTitles: nil, nil] show];
free(alertData);
}
Причина, по которой мы применяем free к переданному нам контексту именно здесь, а не на вызывающей стороне, заключается в том, что вызывающая сторона будет выполнять эту функцию C асинхронно и не сможет узнать, когда выполнение функции на языке C завершится. Поэтому вызывающая сторона должна выделить достаточный объем памяти для контекста AlertViewData (операция malloc), и функция C displayAlertView должна высвободить это пространство.
А теперь вызовем функцию displayAlertView применительно к основной очереди и передадим ей контекст (структуру, содержащую данные для предупреждающего вида):
— (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
AlertViewData *context = (AlertViewData *)
malloc(sizeof(AlertViewData));
if (context!= NULL){
context->title = «GCD»;
context->message = «GCD is amazing.»;
context->cancelButtonTitle = «OK»;
dispatch_async_f(mainQueue,
(void *)context,
displayAlertView);
}
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Если активизировать метод класса currentThread, относящийся к классу NSThread, то выяснится, что блоковые объекты или функции C, направляемые вами в главную очередь, действительно работают в главном потоке:
— (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^(void) {
NSLog(@"Current thread = %@", [NSThread currentThread]);
NSLog(@"Main thread = %@", [NSThread mainThread]);
});
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Вывод данного кода будет примерно таким:
Current thread = <NSThread: 0x4b0e4e0>{name = (null), num = 1}
Main thread = <NSThread: 0x4b0e4e0>{name = (null), num = 1}
Итак, мы изучили, как с помощью GCD решаются задачи, связанные с пользовательским интерфейсом. Перейдем к другим темам — в частности, поговорим о том, как выполнять задачи параллельно, используя параллельные очереди (см. разделы 7.5 и 7.6), и как при необходимости смешивать создаваемый код с кодом пользовательского интерфейса.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКДанный текст является ознакомительным фрагментом.
Читайте также
Обсуждение
Обсуждение Обычно после того, как пользователь успешно снимет фотографию на устройство с iOS, он ожидает, что этот снимок сохранится в его библиотеке фотографий. Однако приложения, не входящие в стандартный комплект программ iOS, могут запросить пользователя сделать снимок
Обсуждение
Обсуждение Фреймворк Assets Library — удобный посредник между разработчиком и библиотекой фотографий. Как будет указано в разделе 13.6, в iOS SDK вам предоставляются встроенные компоненты графического пользовательского интерфейса, которыми можно пользоваться для доступа к
Обсуждение
Обсуждение Чтобы пользователь мог выбирать фотоснимки или видеоролики из своей библиотеки фотографий, необходимо установить свойство sourceType экземпляра UIImagePickerController в значение UIImagePickerControllerSourceTypePhotoLibrary и только потом открывать перед пользователем инструмент для выбора
Обсуждение
Обсуждение Библиотека ресурсов подразделяется на группы. В каждой группе содержатся ресурсы, а каждый ресурс имеет свойства, например URL (универсальные локаторы ресурсов) и объекты представления.Все ресурсы всех типов можно получать из библиотеки ресурсов с помощью
Обсуждение
Обсуждение Класс UIVideoEditorController, содержащийся в iOS SDK, позволяет программисту вывести на экран перед пользователем специальный интерфейс для редактирования. Все, что требуется сделать, — предоставить URL видеоролика, который предполагается отредактировать, а потом
Обсуждение
Обсуждение В зависимости от того, на работу в какой версии iOS рассчитано ваше приложение, его можно запускать и выполнять на различных устройствах, где установлены разные версии iOS. Например, вы можете разрабатывать приложение в последней версии iOS SDK, но в качестве целевой
Обсуждение
Обсуждение Когда приложение переходит в фоновый режим, работа его основного потока приостанавливается. Потоки, которые вы создаете в своем приложении с помощью метода класса detachNewThreadSelector: toTarget: withObject:, относящегося к классу NSThread, также приостанавливаются. Если вы
Обсуждение
Обсуждение Многие приложения, ежедневно поступающие на рынок App Store, обладают возможностями соединения с теми или иными серверами. Некоторые выбирают с сервера данные для обновления, другие отсылают информацию на сервер и т. д. В течение долгого времени в iOS существовал
Обсуждение
Обсуждение В iOS приложение может запросить продолжить воспроизведение своих аудиофайлов, даже если оно само переходит в фоновый режим. В этом разделе мы воспользуемся плеером AVAudioPlayer, который прост и удобен в обращении. Наша задача — запустить аудиоплеер и воспроизвести
Обсуждение
Обсуждение Когда приложение работает в приоритетном режиме, можно получать от экземпляра CLLocationManager делегатные сообщения, информирующие вас о том, что iOS обнаружила перемещение устройства на новое место. Однако если приложение переходит в фоновый режим и становится
Обсуждение
Обсуждение Допустим, пустое приложение iOS (то есть приложение всего с одним окном, для которого еще не написан код) впервые запускается на устройстве с iOS, поддерживающем работу в многозадачном режиме. Оно запускается именно впервые, а не возвращается из фонового режима в
Обсуждение
Обсуждение При работе с приложениями, которые используют класс NSURLConnection, но, уходя в фоновый режим, не запрашивают у iOS дополнительного времени, обращаться с соединениями не составляет никакого труда. Рассмотрим на примере, как будет действовать асинхронное соединение,
Обсуждение
Обсуждение Пока ваше приложение работает в фоновом режиме, может произойти многое! Например, пользователь может вдруг изменить локализацию устройства с iOS на странице Settings (Настройки) и задать, к примеру, испанский язык вместо английского. Приложения могут
Обсуждение
Обсуждение В приложениях, написанных для iOS, файл пакета настроек может быть предоставлен пользователю для внесения собственных настроек. Эти настройки будут доступны пользователю в приложении (Settings) на устройстве. Чтобы лучше понять, как работает этот механизм,
Обсуждение
Обсуждение Пуш-уведомления похожи на локальные уведомления тем, что позволяют сообщать пользователю определенную информацию, даже если ваше приложение неактивно при поступлении уведомления. В то время как локальные уведомления назначаются самим приложением,