文章分享至我的個人技術部落格: https://cainluo.github.io/15019284409430.html
上一章, 我們瞭解到了GCD
裡的一些佇列和任務的知識, 也實踐了一下, 同時我們也對主佇列的一些小情況瞭解了一下, 比如上一章講到的卡執行緒的問題, 如果沒有看的朋友可以去看看玩轉iOS開發:iOS中的GCD開發(二)回顧一下.
這一章, 我們來講講關於GCD
的一些其他小知識.
轉載宣告:如需要轉載該文章, 請聯絡作者, 並且註明出處, 以及不能擅自修改本文.
GCD之間的通訊
在我們日常的iOS
開發裡, 我們一般是會在主執行緒裡重新整理UI
, 比如: 處理按鈕的點選事件, 滾動檢視, 拖拽檢視等等操作.
但其他比較耗時的, 我們都會放在其他執行緒裡進行操作, 比如: 上傳/下載圖片, 上傳/下載檔案等比較耗時的操作.
但這裡還需要配合一下主執行緒來進行操作一番, 比如我們下載完圖片之後, 需要回到主執行緒中重新整理UI
, 這時候我們就需要用到GCD
之間的通訊啦.
不懂? 那我們就來看看程式碼唄, 這裡為了和上一章的工程重複, 我就新建過另一個工程, 取名為GCD-Up-Example
, 機智如我~
- (void)gcdCommunication {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"第%ld次任務的主執行緒為: %@", i, [NSThread currentThread]);
}
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主執行緒, 該執行緒為: %@", [NSThread currentThread]);
});
});
}
複製程式碼
2017-08-05 18:47:11.942 GCD-Up-Example[15176:7745714] 第0次任務的主執行緒為: <NSThread: 0x60000006bb80>{number = 3, name = (null)}
2017-08-05 18:47:11.942 GCD-Up-Example[15176:7745714] 第1次任務的主執行緒為: <NSThread: 0x60000006bb80>{number = 3, name = (null)}
2017-08-05 18:47:11.943 GCD-Up-Example[15176:7745714] 第2次任務的主執行緒為: <NSThread: 0x60000006bb80>{number = 3, name = (null)}
2017-08-05 18:47:11.943 GCD-Up-Example[15176:7745714] 第3次任務的主執行緒為: <NSThread: 0x60000006bb80>{number = 3, name = (null)}
2017-08-05 18:47:11.944 GCD-Up-Example[15176:7745714] 第4次任務的主執行緒為: <NSThread: 0x60000006bb80>{number = 3, name = (null)}
2017-08-05 18:47:11.948 GCD-Up-Example[15176:7745541] 回到主執行緒, 該執行緒為: <NSThread: 0x60000006a1c0>{number = 1, name = main}
複製程式碼
- 從結果裡, 我們可以看到, 所有任務執行完之後, 就會回到主執行緒裡了.
GCD的柵欄方法
有這麼一個場景, 當我們需要進行非同步操作兩組資料時, 要求執行完第一組之後, 才能執行第二組, 那這個咋辦咧?
在GCD
當中, 有一個方法可以解決這種需求, 也就是所謂的柵欄方法
也稱為屏障
, 現在我們來擼一下:
- (void)gcdBarrier {
dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"第一次任務的主執行緒為: %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第二次任務的主執行緒為: %@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"第一次任務, 第二次任務執行完畢, 繼續執行");
});
dispatch_async(queue, ^{
NSLog(@"第三次任務的主執行緒為: %@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"第四次任務的主執行緒為: %@", [NSThread currentThread]);
});
}
複製程式碼
2017-08-05 19:00:44.494 GCD-Up-Example[15252:7757142] 第一次任務的主執行緒為: <NSThread: 0x60000007d580>{number = 3, name = (null)}
2017-08-05 19:00:44.494 GCD-Up-Example[15252:7757144] 第二次任務的主執行緒為: <NSThread: 0x60800007bb00>{number = 4, name = (null)}
2017-08-05 19:00:44.494 GCD-Up-Example[15252:7757144] 第一次任務, 第二次任務執行完畢, 繼續執行
2017-08-05 19:00:44.495 GCD-Up-Example[15252:7757144] 第三次任務的主執行緒為: <NSThread: 0x60800007bb00>{number = 4, name = (null)}
2017-08-05 19:00:44.495 GCD-Up-Example[15252:7757142] 第四次任務的主執行緒為: <NSThread: 0x60000007d580>{number = 3, name = (null)}
複製程式碼
- 通過結果, 我們可以看出, 在執行完第一次和第二次任務後, 會停留一下, 執行完這個
柵欄方法
之後, 才會繼續執行之後的任務.
GCD的延遲方法
有時候, 需要一些延遲操作的場景, 那咋辦咧? 放心~GCD
也有提供這樣子的方法, 並且延遲多少時間是由你說了算:
- (void)gcdAfter {
NSLog(@"我是一個路人");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2秒後執行了.");
});
}
複製程式碼
2017-08-05 19:07:20.039 GCD-Up-Example[15340:7766123] 我是一個路人
2017-08-05 19:07:22.225 GCD-Up-Example[15340:7766123] 2秒後執行了.
複製程式碼
- 看到結果, 我們就知道, 的確是延遲了兩秒後才執行.
- 如果你們不知道在哪裡設定時間的話, 看這句程式碼裡的
2.0
, 這就是設定時間了.
GCD只執行一次的方法
有時候, 我們需要建立一個類, 而這個類的某個方法只可以執行一次, 比如建立一個單例, 怎麼用GCD
解決呢?
既然能夠提到這個問題, 那GCD
肯定也會提供對方的方法嘛, 是吧~
- (void)gcdOne {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 這裡寫只執行一次的任務就好了, 預設是執行緒安全, 所以不用擔心
});
}
複製程式碼
GCD的快速遍歷方法
在GCD
當中, 也有一個類似for
的遍歷方法, 和for
不同, 它幾乎是同時遍歷的.
- (void)gcdApply {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(5, queue, ^(size_t index) {
NSLog(@"執行第%ld任務, 當前執行緒為: %@", index, [NSThread currentThread]);
});
}
複製程式碼
2017-08-05 20:36:08.389 GCD-Up-Example[15555:7794146] 執行第0任務, 當前執行緒為: <NSThread: 0x608000074840>{number = 1, name = main}
2017-08-05 20:36:08.389 GCD-Up-Example[15555:7794186] 執行第1任務, 當前執行緒為: <NSThread: 0x60800007e2c0>{number = 3, name = (null)}
2017-08-05 20:36:08.389 GCD-Up-Example[15555:7794184] 執行第2任務, 當前執行緒為: <NSThread: 0x60800007e200>{number = 4, name = (null)}
2017-08-05 20:36:08.389 GCD-Up-Example[15555:7794183] 執行第3任務, 當前執行緒為: <NSThread: 0x60800007e300>{number = 5, name = (null)}
2017-08-05 20:36:08.389 GCD-Up-Example[15555:7794146] 執行第4任務, 當前執行緒為: <NSThread: 0x608000074840>{number = 1, name = main}
複製程式碼
- 從結果裡我們可以看到, 它幾乎是同時遍歷的, 而且要注意, 這個方法是開啟了新執行緒來進行遍歷的.
GCD的Queue Group
在某個場景下, 我們需要同時非同步執行兩個耗時的任務, 並且在執行完成後直接回到主執行緒, 這個怎麼做呢?
在GCD
中, 有一個叫做佇列組的東西(Queue Group)
, 它就可以滿足我們這個場景需求了.
- (void)gcdQueueGroup {
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group,
queue,
^{
// 執行第一個耗時的任務
});
dispatch_group_async(group,
queue,
^{
// 執行第二個耗時的任務
});
dispatch_group_notify(group,
dispatch_get_main_queue(),
^{
// 回到主執行緒
});
}
複製程式碼
總結
GCD
的一些常用知識基本到這裡, 基本上已經哦了, 如果你還要更深入的去了解的話, 可以去GCD官方文件裡查詢一下你所需要的資料.
再多說一句話, 學習理論知識固然重要, 但要和實際開發使用掛鉤, 不然光學不用, 等於空.
工程地址
專案地址: https://github.com/CainRun/iOS-Project-Example/tree/master/GCD-Up-Example