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