GCD常用用法
GCD我們用的已經很多了,同步非同步,並行序列各位博主也有了很詳細的介紹,我在這裡總結一下平時會常用的幾種方法。
GCD的幾個重要概念:任務、佇列、佇列組、並行、序列、同步、非同步
1.非同步並行,可以開啟多個執行緒,任務交替(同時)執行。
一般用來處理耗時的操作,如網路請求、IO操作、寫入讀取等。
/** 建立一個並行佇列 @param "net.bujige.testQueue" 該佇列的唯一標識字串 @param DISPATCH_QUEUE_CONCURRENT 代表並行佇列 */ dispatch_queue_t queue = dispatch_queue_create("net.ly.testQueue", DISPATCH_QUEUE_CONCURRENT); 或者使用dispatch_get_global_queue來獲取並行佇列 /** 獲取一個並行佇列 @param DISPATCH_QUEUE_PRIORITY_DEFAULT 佇列優先順序 */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) /** 加入兩個非同步任務 */ dispatch_async(queue, ^{ NSLog(@"任務1"); }); dispatch_async(queue, ^{ NSLog(@"任務2"); }); 此時任務一任務二同時執行,不會互相影響。
2.非同步序列,可以開啟一個執行緒,任務一個接一個執行。
我們經常會遇到這種需求,比如一張圖片載入完成之後,再去執行下一張圖片,這時候就可以用到非同步序列。
注意:UI操作都要放到主執行緒。
/** 建立一個序列佇列 @param "net.ly.testQueue" 該佇列的唯一標識字串 @param DISPATCH_QUEUE_SERIAL 代表序列佇列 */ dispatch_queue_t queue = dispatch_queue_create("net.ly.testQueue", DISPATCH_QUEUE_SERIAL); /** 加入兩個非同步任務 */ dispatch_async(queue, ^{ NSLog(@"任務1"); }); dispatch_async(queue, ^{ NSLog(@"任務2"); }); 任務1執行結束之後,任務2開始執行。且均沒有在主執行緒操作。
3.回到主執行緒(執行緒間的通訊)。
之前我們瞭解過,UI操作都要放到主執行緒。必須要用非同步任務,否則會阻塞主執行緒並有可能造成閃退(xcode9以下)
dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"回到主執行緒"); });
4.柵欄方法:dispatch_barrier_async
我們有時需要非同步執行兩組操作,而且第一組操作執行完之後,才能開始執行第二組操作。這樣我們就需要一個相當於柵欄一樣的一個方法將兩組非同步執行的操作組給分割起來,當然這裡的操作組裡可以包含一個或多個任務。這就需要用到dispatch_barrier_async方法在兩個操作組間形成柵欄。
dispatch_barrier_async函式會等待前邊追加到併發佇列中的任務全部執行完畢之後,再將指定的任務追加到該非同步佇列中。然後在dispatch_barrier_async函式追加的任務執行完畢之後,非同步佇列才恢復為一般動作,接著追加任務到該非同步佇列並開始執行。
ofollow,noindex">引用柵欄方法一般用在非同步並行才有意義。用得好的話很強大的功能。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_async(queue, ^{ NSLog(@"任務1"); }); dispatch_async(queue, ^{ NSLog(@"任務2"); }); dispatch_barrier_async(queue, ^{ NSLog(@"柵欄"); }); dispatch_async(queue, ^{ NSLog(@"任務3"); }); dispatch_async(queue, ^{ NSLog(@"任務4"); }); 任務1、2同時執行,且均結束之後,執行柵欄方法,然後才會去執行任務3、任務4
5.延時方法:dispatch_after
最常用到的GCD方法
需要注意的是:dispatch_after函式並不是在指定時間之後才開始執行處理,而是在指定時間之後將任務追加到主佇列中。嚴格來說,這個時間並不是絕對準確的,但想要大致延遲執行任務,dispatch_after函式是很有效的。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ NSLog(@"2s後執行"); });
6.once方法:dispatch_once
一般用在單例、讀取APP資訊時用到,避免重複執行復雜的操作
static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ NSLog(@"執行一次"); });
7.GCD快速遍歷:dispatch_apply
dispatch_apply函式會等待全部任務執行完畢。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(6, queue, ^(size_t) { //迴圈操作 });
一般使用並行佇列。如果是在序列佇列中使用 dispatch_apply,那麼就和 for 迴圈一樣,按順序同步執行。可這樣就體現不出快速迭代的意義了。
8.GCD定時器:
一般開發不會直接使用NSTimer的定時器,加入執行緒,釋放等步驟都會出現一系列細節問題需要注意。而一個封裝好的GCD定時器,會完成我們的大部分需求。
注意:dispatch_source_t物件必須要設定成屬性,不然會被提前釋放掉導致迴圈提前結束
/** 獲取一個並行執行緒 @param DISPATCH_QUEUE_PRIORITY_DEFAULT 執行緒優先順序 */ dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //建立定時器 self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); /** 設定時間屬性 @param sourcedispatch_source_t物件 @param start開始時間 @param interval時間間隔 */ dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 2 * NSEC_PER_SEC, 0); //handler,eventBlock即為定時器的回撥block dispatch_source_set_event_handler(_timer, eventBlock); //取消(_timer會被釋放掉) dispatch_source_cancel(_timer); //暫停 dispatch_suspend(_timer); //繼續 dispatch_resume(_timer);
定時器開始時必須呼叫dispatch_resume方法。
dispatch_suspend之後,重新resume,會補上這段時間缺失的方法。舉個簡單的例子,2s間隔的定時器,suspend10s後resume,會一次性執行5次eventBlock方法。這裡要注意下。
常用的幾個GCD方法就介紹到這裡了,下面我來著重描述下佇列組 的用法,放到下一篇部落格裡面。☺