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

CainLuo發表於2017-08-12

文章分享至我的個人技術部落格: https://cainluo.github.io/15019074509183.html


在前面一篇文章裡, 我們大概知道了GCD是個什麼概念, 也知道了如何建立佇列, 建立任務, 但前面的都只是理念, 估計有很多人不喜, 要開始噴我了, 這才我們來用程式碼實踐實踐, 如果沒有看過上一篇的, 可以去玩轉iOS開發:iOS中的GCD開發(一)看看.

轉載宣告:如需要轉載該文章, 請聯絡作者, 並且註明出處, 以及不能擅自修改本文.


CGD的基本使用

這裡我們就需要建立一個工程, 然後試試水:

並行佇列+同步執行

- (void)syncQueueConcurrent {
    
    NSLog(@"開始執行任務");
    
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第一個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第二個任務當前執行緒為: %@", [NSThread currentThread]);
    });

    dispatch_sync(queue, ^{
        
        NSLog(@"第三個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    NSLog(@"結束執行任務");
}
複製程式碼
2017-08-05 15:03:55.567 GCD-Example[14007:7612071] 開始執行任務
2017-08-05 15:03:55.567 GCD-Example[14007:7612071] 第一個任務當前執行緒為: <NSThread: 0x608000067340>{number = 1, name = main}
2017-08-05 15:03:55.567 GCD-Example[14007:7612071] 第二個任務當前執行緒為: <NSThread: 0x608000067340>{number = 1, name = main}
2017-08-05 15:03:55.568 GCD-Example[14007:7612071] 第三個任務當前執行緒為: <NSThread: 0x608000067340>{number = 1, name = main}
2017-08-05 15:03:55.568 GCD-Example[14007:7612071] 結束執行任務
複製程式碼
  • 從輸出結果, 我們可以看得出, 現在當前的執行緒都為同一條, 名為main, 而且執行緒數只有1.
  • 在執行任務的前和執行任務後, 我都加了一個Log, 從執行順序上我們可以知道, 並行佇列+同步執行是一個任務一個任務的去執行的.

並行佇列+非同步執行

接下來我們看看並行佇列+非同步執行的組合:

- (void)asyncQueueConcurrent {
    
    NSLog(@"開始執行任務");
    
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        
        NSLog(@"第一個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"第二個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"第三個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    NSLog(@"結束執行任務");
}
複製程式碼
2017-08-05 15:08:40.069 GCD-Example[14052:7619255] 開始執行任務
2017-08-05 15:08:40.070 GCD-Example[14052:7619255] 結束執行任務
2017-08-05 15:08:40.070 GCD-Example[14052:7619380] 第三個任務當前執行緒為: <NSThread: 0x60800026a640>{number = 5, name = (null)}
2017-08-05 15:08:40.070 GCD-Example[14052:7619363] 第一個任務當前執行緒為: <NSThread: 0x60800026a5c0>{number = 3, name = (null)}
2017-08-05 15:08:40.070 GCD-Example[14052:7619362] 第二個任務當前執行緒為: <NSThread: 0x60000026bb00>{number = 4, name = (null)}
複製程式碼
  • 從輸出的結果, 我們可以看到, 這裡的執行緒數有好幾條, 而且執行緒的名字也不知道, 並且從時間上來看到, 幾乎都是在同一時間執行任務的.
  • 所以我們可以得出一個結果, 並行佇列+非同步執行的組合除了在主佇列上執行, 還會另外開啟多幾條執行緒來並行執行任務.

序列佇列+同步執行

繼續下一個序列佇列+同步執行組合:

- (void)syncQueueSerial {
    
    NSLog(@"開始執行任務");

    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第一個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第二個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第三個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    NSLog(@"結束執行任務");
}
複製程式碼
2017-08-05 15:16:21.723 GCD-Example[14119:7628753] 開始執行任務
2017-08-05 15:16:21.723 GCD-Example[14119:7628753] 第一個任務當前執行緒為: <NSThread: 0x6080000794c0>{number = 1, name = main}
2017-08-05 15:16:21.724 GCD-Example[14119:7628753] 第二個任務當前執行緒為: <NSThread: 0x6080000794c0>{number = 1, name = main}
2017-08-05 15:16:21.724 GCD-Example[14119:7628753] 第三個任務當前執行緒為: <NSThread: 0x6080000794c0>{number = 1, name = main}
2017-08-05 15:16:21.724 GCD-Example[14119:7628753] 結束執行任務
複製程式碼
  • 從結果看, 這個序列佇列+同步執行組合是一個一個任務來執行的, 都是在主佇列中完成, 並沒有開啟新執行緒.
  • 由於是序列佇列, 所以我們可以在上面看到, 先執行第一個開始Log, 然後任務都是按個新增, 最後在執行結束Log.

序列佇列 + 非同步執行

繼續序列佇列 + 非同步執行:

- (void)asyncQueueSerial {
    
    NSLog(@"開始執行任務");
    
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue, ^{
        
        NSLog(@"第一個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"第二個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"第三個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    NSLog(@"結束執行任務");
}
複製程式碼
2017-08-05 17:32:06.723 GCD-Example[14279:7669095] 開始執行任務
2017-08-05 17:32:06.723 GCD-Example[14279:7669095] 結束執行任務
2017-08-05 17:32:06.723 GCD-Example[14279:7669192] 第一個任務當前執行緒為: <NSThread: 0x60800006ee00>{number = 3, name = (null)}
2017-08-05 17:32:06.724 GCD-Example[14279:7669192] 第二個任務當前執行緒為: <NSThread: 0x60800006ee00>{number = 3, name = (null)}
2017-08-05 17:32:06.724 GCD-Example[14279:7669192] 第三個任務當前執行緒為: <NSThread: 0x60800006ee00>{number = 3, name = (null)}
複製程式碼
  • 從結果來看, 我們可以看到, 是開啟新執行緒來執行任務, 但由於是序列佇列, 所以這裡的任務還是一個接著一個來執行的.
  • 另外, 這裡還有一點, 我們可以看到先列印了開發Log和結束Log, 最後才執行任務, 可以說明, 這裡的任務並不是一下子就開始執行的, 是需要將任務都新增到佇列裡, 然後才開始同步執行.

主佇列

主佇列是GCD自帶的一種特殊的佇列, 這裡有兩點需要注意一下:

  • 所有放在主佇列裡執行的任務都會放到主執行緒裡執行.
  • 我們可以通過dispatch_get_main_queue()獲取主佇列.

說那麼多不如直接來段程式碼:

主佇列+同步執行

- (void)syncMainQueue {
    
    NSLog(@"開始執行任務");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第一個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第二個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_sync(queue, ^{
        
        NSLog(@"第三個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    NSLog(@"結束執行任務");
}
複製程式碼
2017-08-05 17:56:24.607 GCD-Example[14437:7689741] 開始執行任務
複製程式碼

當我們執行的時候發現異常了, 斷言在執行完開始Log之後不動了, 為什麼咧?

其實這是因為我們在ViewDidload方法裡執行了[self syncMainQueue];方法, 我們都知道, 同步執行是一個一個任務去執行的.

但主線還在執行[self syncMainQueue];的時候, 我們又往主執行緒裡塞任務, 這個時候就會出現異常現象, 我們稱之為卡執行緒.

如果要解決的話, 我們就需要在ViewDidload裡改善一下方案:

- (void)viewDidLoad {
    [super viewDidLoad];
    
    dispatch_queue_t queue = dispatch_queue_create("test", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        
        [self syncMainQueue];
    });
}
複製程式碼
2017-08-05 18:03:33.209 GCD-Example[14492:7698292] 開始執行任務
2017-08-05 18:03:33.214 GCD-Example[14492:7698172] 第一個任務當前執行緒為: <NSThread: 0x600000066340>{number = 1, name = main}
2017-08-05 18:03:33.215 GCD-Example[14492:7698172] 第二個任務當前執行緒為: <NSThread: 0x600000066340>{number = 1, name = main}
2017-08-05 18:03:33.216 GCD-Example[14492:7698172] 第三個任務當前執行緒為: <NSThread: 0x600000066340>{number = 1, name = main}
2017-08-05 18:03:33.216 GCD-Example[14492:7698292] 結束執行任務
複製程式碼

唔, 醬紫改造就可以順利的執行完任務了, 這裡還需要提多兩點:

  • 在執行的結果裡, 雖我們在ViewDidload用的是並行+非同步的組合去改善的, 但這不會影響到主佇列裡的任務執行順序, 也不會開啟新執行緒.
  • 主佇列是屬於序列佇列, 所以我們可以看到所執行的任務是一個接著一個.

主佇列+非同步執行

最後一個組合主佇列+非同步執行:

- (void)asyncMainQueue {
    
    NSLog(@"開始執行任務");
    
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    dispatch_async(queue, ^{
        
        NSLog(@"第一個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"第二個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        
        NSLog(@"第三個任務當前執行緒為: %@", [NSThread currentThread]);
    });
    
    NSLog(@"結束執行任務");
}
複製程式碼
2017-08-05 18:08:16.059 GCD-Example[14537:7704614] 開始執行任務
2017-08-05 18:08:16.059 GCD-Example[14537:7704614] 結束執行任務
2017-08-05 18:08:16.064 GCD-Example[14537:7704614] 第一個任務當前執行緒為: <NSThread: 0x608000261700>{number = 1, name = main}
2017-08-05 18:08:16.064 GCD-Example[14537:7704614] 第二個任務當前執行緒為: <NSThread: 0x608000261700>{number = 1, name = main}
2017-08-05 18:08:16.065 GCD-Example[14537:7704614] 第三個任務當前執行緒為: <NSThread: 0x608000261700>{number = 1, name = main}
複製程式碼
  • 從結果上我們可以看到, 雖然我們用的是非同步執行, 具備了開啟新執行緒的能力, 但是由於這是主佇列, 所以所有的任務都是在主執行緒中, 任務也是一個接著一個執行的.
  • 另外, 我們可以看到, 是先執行開始Log, 結束Log, 最後再執行主佇列裡的任務, 這裡我們就可以知道, 任務並不是馬上就執行的, 而是把所有任務都新增到佇列裡之後, 再執行.

總結

學完之後, 我們就可以愉快的和GCD玩耍了, 佇列, 任務神馬的, 再也不用擔心了.


工程地址

專案地址: https://github.com/CainRun/iOS-Project-Example/tree/master/GCD-Example


最後

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

微信

支付寶

相關文章