Книга: iOS. Приемы программирования
Обсуждение
Обсуждение
Иногда необходимо решать задачи, никак не связанные с пользовательским интерфейсом, либо осуществлять процессы, которые взаимодействуют и с пользовательским интерфейсом, но в то же время заняты решением долговременных задач. Например, вам может понадобиться загрузить изображение, а после загрузки отобразить его для пользователя. Процесс загрузки совершенно не связан с пользовательским интерфейсом.
Любая задача, не связанная с пользовательским интерфейсом, позволяет применять глобальные параллельные очереди, которые предоставляет GCD. Они могут выполняться как синхронно, так и асинхронно. Но синхронное выполнение не означает, что программа дожидается, пока выполнится определенный фрагмент кода, а потом продолжает работу. Это лишь означает, что параллельная очередь дождется выполнения вашей задачи и только потом перейдет к выполнению следующего блока кода, стоящего в очереди. Когда вы ставите в параллельную очередь блоковый объект, ваша собственная программа всегда продолжает работу, не дожидаясь, пока выполнится код, стоящий в очереди. Дело в том, что параллельные очереди (как понятно из их названия) выполняют свой код в неглавных потоках. (Из этого правила есть исключение: когда задача передается в параллельную или последовательную очередь посредством функции dispatch_sync, система iOS при наличии такой возможности запускает задачу в текущем потоке. А это может быть и главный поток в зависимости от актуальной ветви кода. Это специальная оптимизация, запрограммированная в GCD, и вскоре мы обсудим ее подробнее.)
Если вы отправляете синхронную задачу в параллельную очередь и в то же время отправляете синхронную задачу в другую параллельную очередь, то две эти синхронные задачи будут выполняться асинхронно друг относительно друга, так как относятся к двум разным параллельным очередям. Этот нюанс важно понимать, поскольку иногда необходимо гарантировать, что задача B начнет выполняться только после того, как завершится задача А. Чтобы обеспечить такую последовательность, эти две задачи нужно синхронно отправлять в одну и ту же очередь.
Синхронные задачи в диспетчерской очереди можно выполнять с помощью функции dispatch_sync. Все, что от вас требуется, — снабдить эту функцию описателем той очереди, в которой должна выполняться задача, а также блоком кода, который должен выполниться в данной очереди.
Рассмотрим пример. Данная функция выводит на консоль числа от 1 до 1000, всю последовательность подряд, и при этом не блокирует основной поток. Мы можем создать блоковый объект, выполняющий подсчет за нас, и синхронно (дважды) вызвать этот же блоковый объект:
void (^printFrom1To1000)(void) = ^{
NSUInteger counter = 0;
for (counter = 1;
counter <= 1000;
counter++){
NSLog(@"Counter = %lu — Thread = %@",
(unsigned long)counter,
[NSThread currentThread]);
}
};
Итак, попробуем активизировать этот блоковый объект с помощью GCD:
— (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
dispatch_queue_t concurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync(concurrentQueue, printFrom1To1000);
dispatch_sync(concurrentQueue, printFrom1To1000);
// Точка переопределения для дополнительной настройки
// после запуска приложения
[self.window makeKeyAndVisible];
return YES;
}
Запустив этот код, вы заметите, что счетчик работает в главном потоке даже при том, что вы поставили эту задачу на выполнение в параллельную очередь. Оказывается, что это явление — специальная оптимизация GCD. Функция dispatch_sync будет использовать актуальный поток, то есть поток, который вы задействуете при направлении задачи в очередь, всякий раз, когда это возможно. В этом и заключается упомянутая оптимизация. Вот что об этом пишет Apple в справке по GCD: «В целях оптимизации работы данная функция активизирует блок кода в актуальном потоке всякий раз, когда это возможно».
Чтобы выполнить вместо блокового объекта функцию на языке C и сделать это синхронно, в диспетчерской очереди, используйте функцию dispatch_sync_f. Давайте просто преобразуем код, написанный для блокового объекта printFrom1To1000, в эквивалентную ему функцию на языке C:
void printFrom1To1000(void *paramContext){
NSUInteger counter = 0;
for (counter = 1;
counter <= 1000;
counter++){
NSLog(@"Counter = %lu — Thread = %@",
(unsigned long)counter,
[NSThread currentThread]);
}
}
А теперь можно воспользоваться функцией dispatch_sync_f для выполнения функции printFrom1To1000 в параллельной очереди:
— (BOOL) application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
dispatch_queue_t concurrentQueue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_sync_f(concurrentQueue,
NULL,
printFrom1To1000);
dispatch_sync_f(concurrentQueue,
NULL,
printFrom1To1000);
self.window = [[UIWindow alloc]
initWithFrame: [[UIScreen mainScreen] bounds]];
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
Первый параметр функции dispatch_get_global_queue указывает приоритет параллельной очереди. Этот показатель GCD должен получить и предоставить программисту. Чем выше приоритет, тем больше квантов процессорного времени будет уделяться коду, выполняемому в этой очереди. В качестве первого параметра функции dispatch_get_global_queue можно использовать любое из следующих значений:
• DISPATCH_QUEUE_PRIORITY_LOW — ваша задача будет получать меньше процессорного времени, чем выделяется на задачу в среднем;
• DISPATCH_QUEUE_PRIORITY_DEFAULT — ваша задача получит стандартный системный приоритет;
• DISPATCH_QUEUE_PRIORITY_HIGH — ваша задача будет получать больше процессорного времени, чем выделяется на задачу в среднем.
Второй параметр функции dispatch_get_global_queue зарезервирован, ему всегда следует передавать значение 0.
В данном разделе было рассмотрено, как передавать задачи в параллельные очереди для синхронного исполнения. В следующем разделе обсудим асинхронное исполнение в параллельных очередях, а в разделе 7.10 будет показано, как исполнять задачи синхронно и асинхронно в последовательных очередях, создаваемых вами для приложений.