關于 iOS 多線程中 GCD 的基礎知識已在上一篇文章中詳細說明,請參看《輕松學iOS多線程之 GCD 的基本使用》進行了解,本文主要對 GCD 中的幾種常用函數做進一步概述。
在 GCD 中的常用函數主要有柵欄函數、一次性函數、延遲執行函數、快速迭代函數,最后包括 隊列組的使用
以及線程間的通信
。
一、柵欄函數
柵欄函數的作用是用來控制任務的執行順序,必須上面的任務 1 執行完畢才執行當前柵欄任務,必須當前柵欄任務執行完畢才執行下面的任務 2。
//1.獲取并發隊列
dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
//2.異步函數
dispatch_async(queue, ^{
NSLog(@"1 --- %@", [NSThread currentThread]);
});
//3.設置柵欄
dispatch_barrier_async(queue, ^{
NSLog(@" ++++++++++++++ ");
});
dispatch_async(queue, ^{
NSLog(@"2 --- %@", [NSThread currentThread]);
});
注意:柵欄函數不能使用全局并發隊列,會喪失攔截功能
二、一次性函數
一次性函數顧名思義就是在程序的運行過程中,一次性函數中的代碼只會執行一次,一次性函數的內部原理就是開始時 onceToken == 0,如果為 0 則執行,執行之后值為 -1,就不執行了。一次性函數在單例中會經常使用。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"只執行一次,默認是線程安全的");
});
注意:一次性函數不能放入懶加載中
三、延遲執行函數
在以往的學習中,我們已經知道了兩種實現延時執行的方法:
// 2秒后再調用self的run方法
[self performSelector:@selector(run) withObject:nil afterDelay:2.0];
// 2秒后再調用self的run方法
[NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
在 GCD 中利用 dispatch_after 也可以實現延遲執行的效果:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"延時 2.0 秒執行");
});
在延遲執行中,dispatch_after 函數本身是一個異步函數,其中的隊列也可以使用自定義的隊列,它的實質是延遲 2 秒再將任務提交到隊列中,從而達到延遲執行的效果。
四、快速迭代函數(遍歷)
在以往我們知道可以使用 for 循環和 block 進行遍歷,在 GCD 中我們也可以使用 dispatch_apply 函數進行快速迭代。
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_apply(10, queue, ^(size_t i) {
NSLog(@"%zd --- %@", i, [NSThread currentThread]);
});
在 dispatch_apply 函數中的三個參數分別代表著需要遍歷的次數,存放任務的隊列以及索引(就相當于 for 循環中的 i )。
注意:1. 快速迭代中的隊列只能使用并發隊列,不要使用串行隊列或主隊列,使用串行隊列時就相當于 for 循環,使用主隊列會造成死鎖
。
2. 在快速迭代中,會開啟子線程與主線程一起并發執行任務,執行順序是不固定的
。
五、隊列組的基本使用
需求:同時下載圖片一和圖片二,當兩張圖片均下載完畢后合成使之成為一張圖片。
在以前我們可以利用 GCD 中的柵欄函數解決這個問題,具體的思路為:使用異步函數 + 并發隊列開啟子線程來同時下載圖片,然后添加一個柵欄函數進行攔截,最后在柵欄函數之后進行合成圖片的任務,這里需要注意的是在合成圖片的子線程中需要回到主線程(異步函數 + 主隊列)進行展示圖片。
其實除此之外,我們也可以利用隊列組來解決這個問題,當隊列組中所有的任務都執行完畢,就會執行 dispatch_group_notify 函數。
//創建隊列組
dispatch_group_t group = dispatch_group_create();
//創建并發隊列
dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
//將任務添加到隊列組中
dispatch_group_async(group, queue, ^{
NSLog(@"1 --- %@", [NSThread currentThread]);
});
//該函數本身是異步函數
dispatch_group_notify(group, queue, ^{
NSLog(@"--- end --- %@", [NSThread currentThread]);
});
六、線程間的通信
在子線程中下載圖片回到主線程設置并顯示:
//1.獲取隊列(串行|并行) 0 就是異步函數
dispatch_queue_t queue = dispatch_queue_create(0, 0);
//2.異步函數封裝任務提交到隊列
dispatch_async(queue, ^{
//3.獲取 URL
NSURL *url = [NSURL URLWithString:@"http://c.hiphotos.baidu.com/image/pic/item/3b292df5e0fe99254674c15036a85edf8db171b2.jpg"];
//4.下載二進制數據
NSData *imageData = [NSData dataWithContentsOfURL:url];
//5.將二進制數據轉換為圖片
UIImage *image = [UIImage imageWithData:imageData];
//6.回到主線程設置圖片(異步函數 + 主隊列)
dispatch_async(dispatch_get_main_queue(), ^{
self.imageView.image = image;
NSLog(@"%@", [NSThread currentThread]);
});
});