玩轉iOS開發:iOS中的GCD開發(三)

CainLuo發表於2017-08-13

文章分享至我的個人技術部落格: 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


最後

碼字很費腦, 看官賞點飯錢可好

微信

支付寶

相關文章