Обсуждение

We use cookies. Read the Privacy and Cookie Policy

Обсуждение

Задачи, связанные с пользовательским интерфейсом, должны выполняться в главном потоке. Поэтому единственным каналом для передачи в 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), и как при необходимости смешивать создаваемый код с кодом пользовательского интерфейса.

Данный текст является ознакомительным фрагментом.