GCD,全稱Grand Central Dispath,是蘋果開發的一種支援並行操作的機制。它的主要部件是一個FIFO佇列和一個執行緒池,前者用來新增任務,後者用來執行任務。
GCD中的FIFO佇列稱為dispatch queue,它可以保證先進來的任務先得到執行(但不保證一定先執行結束)。
透過與執行緒池的配合,dispatch queue分為下面兩種:
- Serial Dispatch Queue -- 執行緒池只提供一個執行緒用來執行任務,所以後一個任務必須等到前一個任務執行結束才能開始。
- Concurrent Dispatch Queue -- 執行緒池提供多個執行緒來執行任務,所以可以按序啟動多個任務併發執行。
1. Basic Management
我們可以透過dispatch_queue_cretae來建立佇列,然後用dispatch_release釋放。比如下面兩段程式碼分別建立序列佇列和並行佇列:
dispatch_queue_t serialQ = dispatch_queue_create("eg.gcd.SerialQueue", DISPATCH_QUEUE_SERIAL); dispatch_async(serialQ, ^{ // Code here }); dispatch_release(serialQ); dispatch_queue_t concurrentQ = dispatch_queue_create("eg.gcd.ConcurrentQueue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(concurrentQ, ^{ // Code here }); dispatch_release(concurrentQ);
而系統預設就有一個序列佇列main_queue和並行佇列global_queue:
dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_queue_t mainQ = dispatch_get_main_queue();
通常,我們可以在global_queue中做一些long-running的任務,完成後在main_queue中更新UI,避免UI阻塞,無法響應使用者操作:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // long-running task dispatch_async(dispatch_get_main_queue(), ^{ // update UI }); });
上面提到dispatch_async這個介面,用來提交blcok給指定queue進行非同步執行。這個介面會在成功提交block後立即返回,然後繼續執行下去。由於block是定義在棧上的,所以需要將其複製到堆上,見這裡。
與之相對應的是dispatch_sync介面,提交block以供同步執行。這個介面會等到block執行結束才返回,所以不需要複製block。So,如果在呼叫該介面在當前queue上指派任務,就會導致deadlock。維基百科上給了段示例程式碼:
dispatch_queue_t exampleQueue = dispatch_queue_create("com.example.unique.identifier", NULL ); dispatch_sync( exampleQueue,^{ dispatch_sync( exampleQueue,^{ printf("I am now deadlocked...\n"); });}); dispatch_release( exampleQueue );
如果追求的是併發,那麼dispatch_sync有什麼用呢?關於dispatch_sync的用途,SO上有討論。
2. Normal Control
- dispatch_once
如果沒有記錯的話,在iOS Con 2012上,大眾點評的同學分享了個Topic叫《iOS開發最佳實踐》,開篇講singleton實現的演進(怎麼演進都有可以挑的刺),後面轉折說要把精力放到使用者看得到的地方。
如果把singleton和best practice放在一起,那麼我很容易聯想到dispatch_once這個函式,它可以保證整個應用程式生命週期中某段程式碼只被執行一次!
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ // code to be executed once });
- dispatch_after
有時候我們需要等個幾秒鐘然後做個動畫或者給個提示,這時候可以用dispatch_after這個函式:
double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC); dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // code to be executed on the main queue after delay });
- dispatch_set_target_queue
透過dispatch_set_target_queue函式可以設定一個dispatch queue的優先順序,或者指定一個dispatch source相應的事件處理提交到哪個queue上。
dispatch_set_target_queue(serialQ, globalQ);
- dispatch_apply
執行某個程式碼片段若干次。
dispatch_apply(10, globalQ, ^(size_t index) { // do sth. 10 times });
- dispatch group
Dispatch Group機制允許我們監聽一組任務是否完成:
dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, concurrentQ, blk0); dispatch_group_async(group, concurrentQ, blk1); dispatch_group_async(group, concurrentQ, blk2); dispatch_group_notify(group, mainQ, ^{ // update UI }); dispatch_release(group);
或者說同步地等待一段時間看是否結束:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 1ull * NSEC_PER_SEC);
dispatch_group_wait(group, time);
- dispatch_barrier_async
透過dispatch_barrier_async函式提交的任務會等它前面的任務執行結束才開始,然後它後面的任務必須等它執行完畢才能開始。
dispatch_async(concurrentQ, blk0);
dispatch_async(concurrentQ, blk1);
dispatch_barrier_async(concurrentQ, blk_barrier);
dispatch_async(concurrentQ, blk2);
原文連結:http://blog.csdn.net/jasonblog/article/details/7816999
最後,推薦一個神器。
內測寶
內測寶, 個人覺得比TestFlight更簡單好用,開發者只需要簡單把打好的ipa包上傳上去,生成二維碼,測試人員在手機上掃碼二維碼,就可以直接安裝最新的測試版本了,好用的讓人想哭。