GCD我們用的已經(jīng)很多了,同步異步,并行串行各位博主也有了很詳細(xì)的介紹,我在這里總結(jié)一下平時(shí)會(huì)常用的幾種方法。
GCD的幾個(gè)重要概念:任務(wù)、隊(duì)列、隊(duì)列組、并行、串行、同步、異步
1.異步并行,可以開(kāi)啟多個(gè)線程,任務(wù)交替(同時(shí))執(zhí)行。
一般用來(lái)處理耗時(shí)的操作,如網(wǎng)絡(luò)請(qǐng)求、IO操作、寫入讀取等。
/**
創(chuàng)建一個(gè)并行隊(duì)列
@param "net.bujige.t7estQueue" 該隊(duì)列的唯一標(biāo)識(shí)字符串
@param DISPATCH_QUEUE_CONCURRENT 代表并行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("net.ly.testQueue", DISPATCH_QUEUE_CONCURRENT);
或者使用dispatch_get_global_queue來(lái)獲取并行隊(duì)列
/**
獲取一個(gè)并行隊(duì)列
@param DISPATCH_QUEUE_PRIORITY_DEFAULT 隊(duì)列優(yōu)先級(jí)
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
/**
加入兩個(gè)異步任務(wù)
*/
dispatch_async(queue, ^{
NSLog(@"任務(wù)1");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
});
此時(shí)任務(wù)一任務(wù)二同時(shí)執(zhí)行,不會(huì)互相影響。
2.異步串行,可以開(kāi)啟一個(gè)線程,任務(wù)一個(gè)接一個(gè)執(zhí)行。
我們經(jīng)常會(huì)遇到這種需求,比如一張圖片加載完成之后,再去執(zhí)行下一張圖片,這時(shí)候就可以用到異步串行。
注意:UI操作都要放到主線程。
/**
創(chuàng)建一個(gè)串行隊(duì)列
@param "net.ly.testQueue" 該隊(duì)列的唯一標(biāo)識(shí)字符串
@param DISPATCH_QUEUE_SERIAL 代表串行隊(duì)列
*/
dispatch_queue_t queue = dispatch_queue_create("net.ly.testQueue", DISPATCH_QUEUE_SERIAL);
/**
加入兩個(gè)異步任務(wù)
*/
dispatch_async(queue, ^{
NSLog(@"任務(wù)1");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
});
任務(wù)1執(zhí)行結(jié)束之后,任務(wù)2開(kāi)始執(zhí)行。且均沒(méi)有在主線程操作。
3.回到主線程(線程間的通信)。
之前我們了解過(guò),UI操作都要放到主線程。必須要用異步任務(wù),否則會(huì)阻塞主線程并有可能造成閃退(xcode9以下)
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"回到主線程");
});
4.柵欄方法:dispatch_barrier_async
我們有時(shí)需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后,才能開(kāi)始執(zhí)行第二組操作。這樣我們就需要一個(gè)相當(dāng)于柵欄一樣的一個(gè)方法將兩組異步執(zhí)行的操作組給分割起來(lái),當(dāng)然這里的操作組里可以包含一個(gè)或多個(gè)任務(wù)。這就需要用到dispatch_barrier_async方法在兩個(gè)操作組間形成柵欄。
dispatch_barrier_async函數(shù)會(huì)等待前邊追加到并發(fā)隊(duì)列中的任務(wù)全部執(zhí)行完畢之后,再將指定的任務(wù)追加到該異步隊(duì)列中。然后在dispatch_barrier_async函數(shù)追加的任務(wù)執(zhí)行完畢之后,異步隊(duì)列才恢復(fù)為一般動(dòng)作,接著追加任務(wù)到該異步隊(duì)列并開(kāi)始執(zhí)行。
引用
柵欄方法一般用在異步并行才有意義。用得好的話很強(qiáng)大的功能。
特殊用法:dispatch_barrier_async可以用來(lái)加鎖。
注意:官方規(guī)定,柵欄函數(shù)不能作用于全局并發(fā)隊(duì)列,也就是dispatch_get_global_queue
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
NSLog(@"任務(wù)1");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)2");
});
dispatch_barrier_async(queue, ^{
NSLog(@"柵欄");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)3");
});
dispatch_async(queue, ^{
NSLog(@"任務(wù)4");
});
任務(wù)1、2同時(shí)執(zhí)行,且均結(jié)束之后,執(zhí)行柵欄方法,然后才會(huì)去執(zhí)行任務(wù)3、任務(wù)4
5.延時(shí)方法:dispatch_after
最常用到的GCD方法
需要注意的是:dispatch_after函數(shù)并不是在指定時(shí)間之后才開(kāi)始執(zhí)行處理,而是在指定時(shí)間之后將任務(wù)追加到主隊(duì)列中。嚴(yán)格來(lái)說(shuō),這個(gè)時(shí)間并不是絕對(duì)準(zhǔn)確的,但想要大致延遲執(zhí)行任務(wù),dispatch_after函數(shù)是很有效的。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"2s后執(zhí)行");
});
6.once方法:dispatch_once
一般用在單例、讀取APP信息時(shí)用到,避免重復(fù)執(zhí)行復(fù)雜的操作
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"執(zhí)行一次");
});
7.GCD快速遍歷:dispatch_apply
dispatch_apply函數(shù)會(huì)等待全部任務(wù)執(zhí)行完畢。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(6, queue, ^(size_t) {
//循環(huán)操作
});
一般使用并行隊(duì)列。如果是在串行隊(duì)列中使用 dispatch_apply,那么就和 for 循環(huán)一樣,按順序同步執(zhí)行??蛇@樣就體現(xiàn)不出快速迭代的意義了。
8.GCD定時(shí)器:
一般開(kāi)發(fā)不會(huì)直接使用NSTimer的定時(shí)器,加入線程,釋放等步驟都會(huì)出現(xiàn)一系列細(xì)節(jié)問(wèn)題需要注意。而一個(gè)封裝好的GCD定時(shí)器,會(huì)完成我們的大部分需求。
注意:dispatch_source_t對(duì)象必須要設(shè)置成屬性,不然會(huì)被提前釋放掉導(dǎo)致循環(huán)提前結(jié)束
/**
獲取一個(gè)并行線程
@param DISPATCH_QUEUE_PRIORITY_DEFAULT 線程優(yōu)先級(jí)
*/
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//創(chuàng)建定時(shí)器
self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
/**
設(shè)置時(shí)間屬性
@param source dispatch_source_t對(duì)象
@param start 開(kāi)始時(shí)間
@param interval 時(shí)間間隔
*/
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), 2 * NSEC_PER_SEC, 0);
//handler,eventBlock即為定時(shí)器的回調(diào)block
dispatch_source_set_event_handler(_timer, eventBlock);
//取消(_timer會(huì)被釋放掉)
dispatch_source_cancel(_timer);
//暫停
dispatch_suspend(_timer);
//繼續(xù)
dispatch_resume(_timer);
定時(shí)器開(kāi)始時(shí)必須調(diào)用dispatch_resume方法。
dispatch_suspend之后,重新resume,會(huì)補(bǔ)上這段時(shí)間缺失的方法。舉個(gè)簡(jiǎn)單的例子,2s間隔的定時(shí)器,suspend10s后resume,會(huì)一次性執(zhí)行5次eventBlock方法。這里要注意下。
常用的幾個(gè)GCD方法就介紹到這里了,下面我來(lái)著重描述下隊(duì)列組的用法,放到下一篇博客里面。?