Grand Central Dispatch — пример использования iOS
Давайте создадим пример того, что мы рассматривали в статье Grand Central Dispatch в iOS на Swift. Очереди
Начнем с создания собственной очереди.
Для создания очереди нам требуется указать имя очереди, что как мы помним облегчит жизнь при отладке iOS приложения. Укажем тип очереди, serieal (по-умолчанию) или concurrent и тип очереди, определяемый QOS.
// // proSwift.ru // let attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0) let primeQueue = dispatch_queue_create("ru.proSwift.primer", attr)
Имя очереди
Давайте создадим вспомогательную функцию для вывода в консоль имени текущей очереди.
// // proSwift.ru // func currentQueueName() -> String? { let label = dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL) return String(CString: label, encoding: NSUTF8StringEncoding) }
Думаю, тут нет ничего сложного. Единственное, что стоит хорошо понимать, что GCD это набор функций языка программирования C. Поэтому нужно сделать преобразование строки из типа CString в String.
Теперь давайте опробуем в деле нашу новую функцию.
// // proSwift.ru // let currentQueueNameString = currentQueueName() print(currentQueueNameString) // 1 dispatch_sync(primeQueue) { print(currentQueueName()) // 2 }
В данном коде в первом варианте мы запросили имя основной очереди, в которой выполняется программа, а во втором случае то же самое мы запросили уже в собственной очереди. Результат в консоли будет выглядеть так:
// // proSwift.ru // Optional("com.apple.main-thread") // 1 Optional("ru.proSwift.primer") // 2
Собственно, результат вполне предсказуем.
ASYNC vs SYNC
Стоит на примере разобраться чем отличаются функции dispatch_async и dispatch_sync. Для этого будем использовать нашу созданную выше очередь. Этот код мы будем использовать для того, чтобы увидеть начало и окончание передачи задач в очередь, а также момент окончания выполнения задачи. Строки, которые попадают в консоль и комментарии говорят сами за себя. По коду ниже не должно возникнуть вопросов.
// // proSwift.ru // // ASYNC print("=== ASYNC:: Асинхронно посылаем в очередь primer ===") dispatch_async(primeQueue) { print(" - - - async:: Выполняем в очереди \(currentQueueName()!)") } print("=== ASYNC:: Закончили асинхронно передавать задачу в очередь primer ===") // SYNC print("=== SYNC:: Синхронно посылаем в очередь primer ===") dispatch_sync(primeQueue) { print(" - - - sync:: Выполняем в очереди \(currentQueueName()!)") } print("=== SYNC:: Закончили синхронно передавать задачу в очередь primer ===\n")
Внимательно посмотрите на результат работы этой части программы:
=== ASYNC:: Асинхронно посылаем в очередь primer === === ASYNC:: Закончили асинхронно передавать задачу в очередь primer === - - - async:: Выполняем в очереди ru.proSwift.primer === SYNC:: Синхронно посылаем в очередь primer === - - - sync:: Выполняем в очереди ru.proSwift.primer === SYNC:: Закончили синхронно передавать задачу в очередь primer ===
Хорошо видно, что в блоке ASYNC консоль отобразила сообщение о начале передачи задачи, об окончании передачи, и лишь потом появилось сообщение о выполнении задачи.
В блоке SYNC напротив все сообщения идут в последовательности действий. Т.е. сначала сообщение о начале передачи задачи, потом выполнение задачи, очередь ждала полного выполнения этой задачи, и только после этого продолжилось выполнение остального кода программы, где вышло сообщение об окончании передачи задачи в очередь.
SERIAL vs CONCURRENT
Для демонстрации работы разного типа очередей создадим функцию hardJob(), в которой будем останавливать поток на одну секунду с помощью функции sleep(1), и после паузы выводить сообщение об окончании работы. Для удобства я также добавил в лог имя очереди и время, которое было затрачено на выполнение задачи. Вывод времени реализуется с помощью функций startClock() — которая запоминает момент времени «сейчас» и showClock(), которая выводит время, прошедшее с момента выполнения startClock().
В примере выше мы создали очередь типа Serial, поэтому ее мы опробуем первой. Пошлем четыре раза задачу hardJob() в нашу очередь.
Затем в блоке //CONCURRENT мы создадим очередь Concurrent и также пошлем задачу hardJob() во вновь созданную очередь. У меня получилось, что я послал ее пять раз. Но для нашего примера это не принципиально.
// // proSwift.ru // func hardJob() { sleep(1) print("\(showClock()) :: \(currentQueueName()!) :: Закончили!") } // SERIAL // наша primeQueue типа Serial, поэтому попробуем функционал именно этой очереди startClock() //Обнуление таймера print("=== Начало выполнения Serial - очереди ===") dispatch_async(primeQueue, hardJob) dispatch_async(primeQueue, hardJob) dispatch_async(primeQueue, hardJob) dispatch_async(primeQueue, hardJob) print(" - = - = - = - = - = - = - = - = - = - = - = - = - = - ") sleep(5) // перерыв пять секунд между работой с разными типами очередей для удобства восприятия лога вывода в консоль. // CONCURRENT let concurrentQueue = dispatch_queue_create("ru.proSwift.concurrent", DISPATCH_QUEUE_CONCURRENT) startClock() //Обнуление таймера print("=== Начало выполнения concurrent - очереди ===") dispatch_async(concurrentQueue, hardJob) dispatch_async(concurrentQueue, hardJob) dispatch_async(concurrentQueue, hardJob) dispatch_async(concurrentQueue, hardJob) dispatch_async(concurrentQueue, hardJob) print(" - = - = - = - = - = - = - = - = - = - = - = - = - = - ")
Результат работы последней части программы:
=== Начало выполнения Serial - очереди === - = - = - = - = - = - = - = - = - = - = - = - = - = - 1.00458300113678 :: ru.proSwift.primer :: Закончили! 2.00884199142456 :: ru.proSwift.primer :: Закончили! 3.01151996850967 :: ru.proSwift.primer :: Закончили! 4.01502102613449 :: ru.proSwift.primer :: Закончили! === Начало выполнения concurrent - очереди === - = - = - = - = - = - = - = - = - = - = - = - = - = - 1.00319999456406 :: ru.proSwift.concurrent :: Закончили! 1.00575304031372 :: ru.proSwift.concurrent :: Закончили! 1.00585901737213 :: ru.proSwift.concurrent :: Закончили! 1.00587600469589 :: ru.proSwift.concurrent :: Закончили! 1.00663203001022 :: ru.proSwift.concurrent :: Закончили!
Итак, видно, что все задачи в очереди ru.proSwift.primer выполняются последовательно, и на выполнение всех четырех потребовалось 4 секунды. А в очереди ru.proSwift.concurrent задачи выполнились за секунду, даже с учетом того, что их пять.
И еще момент, внимательный читатель заметит, что строка кода, которая выводит в консоль линию отреза …
print(" - = - = - = - = - = - = - = - = - = - = - = - = - = - ")
… идет после добавления задач в очередь, а при выводе лога в консоль линия отреза выводится сразу после сообщения о передачи задач в очередь. Причем тип очередь никак не влияет на эту ситуацию. Почему так происходит?
Оставляю читателям возможность перечитать эту статью еще раз и поразмышлять об этом в комментариях…
Добрый день.
Пытался организовать синхронную загрузку данных с помощью Alamofire и не получилось.
Вы не могли бы показать, как это можно сделать?
Как я понял, основная сложность заключается в том, что в файле Download.swift фреймворка организованы свои механизмы асинхронной загрузки. И как их обойти большой вопрос.
Мне бы хотелось, чтобы пока грузятся данных из инета отображался прогрессбар и никакие действия в основном интерфейсе не были доступны (т.е. чтобы я, например, не мог перейти на другой вью при нажатии на одном из item’во в таб баре).
p.s. Если нужен пример того, что я написал, то могу скинуть.
Могу выложить пример кода загрузки картинки с отображением прогресс бара. К сожалению написать полноценную статью с комментариями и разбором полетов — просто не хватает времени.
Как и обещал, код процедуры загрузки картинки с использованием Alamofire и прогресс баром.
http://proswift.ru/primer-metoda-zagruzki-kartinki-alamofire-s-progress-barom/
Спасибо!!!
Сейчас посмотрю.