iOS開發(fā)_GCD、dispatch函數(shù)介紹

iOS多線程的方法有3種:

NSThread

NSOperation

GCD(Grand Central Dispatch)

?其中,由蘋果所倡導的為多核的并行運算提出的解決方案:GCD能夠訪問線程池,并且可在應(yīng)用的整個生命的周期里面使用,一般來說,GCD會盡量維護一些適合機器體系結(jié)構(gòu)的線程,在有工作需求的時候,自動利用更多的處理器核心,以此來充分使用更強大的機器系統(tǒng)性能。在以前,iOS設(shè)備為單核處理器的,線程池的用處并不大,但是現(xiàn)在的移動設(shè)備,包括iOS設(shè)備,愈發(fā)地朝多核的方向邁進,因此GCD中的線程池,能夠在此類設(shè)備中,能夠使得強大的硬件系統(tǒng)性能上得到更加完善的利用。

GCD,無疑是最便捷的,基于C語言的所設(shè)計的。在使用GCD的過程中,最方便的,莫過于不需要編寫基礎(chǔ)線程代碼,其生命周期也不需要手動管理;創(chuàng)建需要的任務(wù),然后添加到已創(chuàng)建好的queue隊列,GCD便會負責創(chuàng)建線程和調(diào)度任務(wù),由系統(tǒng)直接提供線程管理。

這樣一種多線程的方式,我們也會在實際項目中經(jīng)??吹剑篴pp中,由于數(shù)據(jù)的執(zhí)行與交換所消耗的時間長,導致需要反饋給用戶UI界面往往出現(xiàn)延遲的現(xiàn)象。這樣我們可以通過多線程的方法,讓需要調(diào)用的方法在后臺執(zhí)行、在主線程上進行UI界面的切換,這樣不僅是用戶體驗更加友好美觀,也使得程序設(shè)計井然有序。

本文主要粗略介紹GCD的一般使用,以及GCD中dispatch_前綴方法調(diào)用的作用和使用范圍。

UI界面如下圖,通過創(chuàng)建4個按鈕事件,分析4種不同的函數(shù)所執(zhí)行的程序塊運行方式:

【本次開發(fā)環(huán)境: Xcode:7.2 ? ??iOS Simulator:iphone6 ? By:啊左】


一、GCD的使用

GCD對于開發(fā)者來說,最簡單的,就是通過調(diào)用dispatch把一連串的異步任務(wù)添加到隊列中,進行異步執(zhí)行操作。

代碼調(diào)用如下:

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

async表示異步運行;

queue為我們提前創(chuàng)建的隊列;

block也就是“塊”,讓我們執(zhí)行事件的模塊;

async(異步)與sync(同步):

當然,我們也可以使用同步任務(wù),使用dispatch_sync函數(shù)添加到相應(yīng)的隊列中,而這個函數(shù)會阻塞當前調(diào)用線程,直到相應(yīng)任務(wù)完成執(zhí)行。

但是,也正因為這樣的同步特性,在實際項目中,當有同步任務(wù)添加到正在執(zhí)行同步任務(wù)的隊列時,串行的隊列會出現(xiàn)死鎖。而且由于同步任務(wù)會阻塞主線程的運行,可能會導致某個事件無法響應(yīng)。

隊列(queue):

需要注意的是,調(diào)用dispatch_async不會讓塊運行,而是把塊添加到隊列末尾。隊列不是線程,它的作用是組織塊。(如果讀者學過數(shù)據(jù)結(jié)構(gòu)的知識,就會知道隊列的基本特征如飯?zhí)门抨犼?,先到的排前面,先打到飯,也就是“先進先出”原理)

在GCD中,可以給開發(fā)者調(diào)用的常見公共隊列有以下兩種:

dispatch_get_global_queue:用于獲取應(yīng)用全局共享的并發(fā)隊列(提供多個線程來執(zhí)行任務(wù),所以可以按序啟動多個任務(wù)并發(fā)執(zhí)行。可用于后臺執(zhí)行任務(wù))

dispatch_get_main_queue: ? 用于獲取應(yīng)用主線程關(guān)聯(lián)的串行調(diào)度隊列(只提供一個線程執(zhí)行任務(wù)。運行的main主線程,一般用于UI的搭建)

(還有另外一種,dispatch_get_current_queue,用于獲取當前正在執(zhí)行任務(wù)的隊列,主要用于調(diào)試,但是在iOS 6.0之后蘋果已經(jīng)廢棄,原因是容易造成死鎖。詳情可以查看官方注釋。)

這兩種公共隊列的調(diào)用便可以解決我們剛剛關(guān)于后臺執(zhí)行任務(wù)、主線程用于更新UI界面的問題,

結(jié)構(gòu)如下:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{

//把邏輯計算等需要消耗長時間的任務(wù),放在此處的全局共享的并發(fā)隊列執(zhí)行;dispatch_async(dispatch_get_main_queue(), ^{

//回到主線程更新UI界面;

});

});

例如在有一些項目中,會涉及到異步下載圖片,這個時候就可以使用這樣一種結(jié)構(gòu)來進行任務(wù)的分配:

//異步下載圖片dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^{//先把下載數(shù)據(jù)的任務(wù)放在全局共享并發(fā)隊列中執(zhí)行

NSURL *url = [NSURL URLWithString:@"圖片的URL"];

NSData* data =[[NSData alloc]initWithContentsOfURL:url];

UIImage*image =[UIImage imageWithData:data];if(data !=nil)

{//完成后,回到主線程顯示圖片

dispatch_async(dispatch_get_main_queue(), ^{

self.imageView.image=image;

});

}

});

二、串行隊列 and ?并行隊列

1.串行(Serial)的執(zhí)行:指同一時間每次只能執(zhí)行一個任務(wù)。?線程池只提供一個線程用來執(zhí)行任務(wù),所以后一個任務(wù)必須等到前一個任務(wù)執(zhí)行結(jié)束才能開始。

可以添加多個任務(wù)到隊列中,執(zhí)行次序FIFO,但是當程序需要執(zhí)行大量的任務(wù)時,雖然系統(tǒng)允許,但是鑒于程序的資源分配,應(yīng)該交給全局并發(fā)隊列來完成才能更好地發(fā)揮系統(tǒng)性能。

創(chuàng)建串行隊列的方式如下:

dispatch_queue_t serialQueue = dispatch_queue_create("zuoA", NULL);//第一個參數(shù)是隊列的名稱,通常使用公司的反域名;第二個參數(shù)是隊列相關(guān)屬性,一般用NULL.

關(guān)于什么是FIFO次序,我們用代碼解釋一下

- (IBAction)SerialQueue:(UIButton *)sender {

dispatch_queue_t serialQueue= dispatch_queue_create("zuoA", NULL);

dispatch_async(serialQueue,^{

sleep(3);

NSLog(@"A任務(wù)");

});

dispatch_async(serialQueue,^{

sleep(2);

NSLog(@"B任務(wù)");

});

dispatch_async(serialQueue,^{

sleep(1);

NSLog(@"C任務(wù)");

});

}

console控制臺顯示如下:

2016-03-15 15:04:11.909 dispatch_queue的多任務(wù)GCD使用[92316:2538875] A任務(wù)

2016-03-15 15:04:13.910 dispatch_queue的多任務(wù)GCD使用[92316:2538875] B任務(wù)

2016-03-15 15:04:14.910 dispatch_queue的多任務(wù)GCD使用[92316:2538875] C任務(wù)

可以看得到,即使需要等待幾秒,后面所添加的任務(wù)也必須等待前面的任務(wù)完成后才能執(zhí)行,類似我們前面所講"飯?zhí)?排隊的例子,隊列完全按照"先進先出"的順序,也即是所執(zhí)行的順序取決于:開發(fā)者將工作任務(wù)添加進隊列的順序。

2.并行(concurrent)的執(zhí)行:可同一時間可以同時執(zhí)行多個任務(wù)。

負荷:并發(fā)執(zhí)行任務(wù)與系統(tǒng)有關(guān),能夠同時執(zhí)行任務(wù)的數(shù)量是由系統(tǒng)根據(jù)應(yīng)用和此時的系統(tǒng)狀態(tài)等動態(tài)變化決定的。

順序:由于并行隊列也是隊列(吐槽這是廢話T^T),因此每個任務(wù)的啟動時間也是按照FIFO次序,也就是加入queue的順序,但是結(jié)束的順序則依 ? ? ? ? ?賴各自的任務(wù)所需要消耗的時間。

與串行的不同:雖然啟動時間一致,但是這是“并發(fā)執(zhí)行”,因此不需要等到上一個任務(wù)完成后才進行下一個任務(wù)。所以每個塊中的各部分的先后執(zhí)行的順序需要視情況而定。

上代碼,找不同。。。

- (IBAction)concurrentQueue:(UIButton *)sender {

dispatch_queue_t concurrentQueue

=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_async(concurrentQueue,^{

dispatch_async(concurrentQueue,^{

sleep(3);

NSLog(@"A任務(wù)");

});

dispatch_async(concurrentQueue,^{

sleep(2);

NSLog(@"B任務(wù)");

});

dispatch_async(concurrentQueue,^{

sleep(1);

NSLog(@"C任務(wù)");

});

});

}

console控制臺顯示如下:

2016-03-15 15:02:06.911 dispatch_queue的多任務(wù)GCD使用[92294:2537296] C任務(wù)

2016-03-15 15:02:07.907dispatch_queue的多任務(wù)GCD使用[92294:2537147] B任務(wù)

2016-03-15 15:02:08.908dispatch_queue的多任務(wù)GCD使用[92294:2537177] A任務(wù)

通過控制臺左邊的時間記錄,可以看到,與串行隊列不同的是,并行隊列中這3個任務(wù)的并行啟用,與串行不同的是,不需要等到A任務(wù)調(diào)用完,就已經(jīng)在調(diào)用B、C,顯著地提高了線程的執(zhí)行速度,凸顯了并行隊列所執(zhí)行的異步操作的并行特性;

另外,從這段代碼中,不同的是串行隊列需要創(chuàng)建一個新的隊列,而并行隊列中,只需要調(diào)用iOS系統(tǒng)中為我們提供的全局共享dispatch_get_global_queue就可以了:

dispatch_queue_t concurrentQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

第一個參數(shù)為iOS系統(tǒng)為全局共享隊列提供4種調(diào)度的方式,主要區(qū)別即是優(yōu)先級的不同而已:

DISPATCH_QUEUE_PRIORITY_HIGH

DISPATCH_QUEUE_PRIORITY_DEFAULT

DISPATCH_QUEUE_PRIORITY_LOW

DISPATCH_QUEUE_PRIORITY_BACKGROUND

我們采用默認的DISPATCH_QUEUE_PRIORITY_DEFAULT方式,而右邊的第二個參數(shù)是蘋果預留的,暫時沒有其他的含義,所以,一般默認為:0。

并發(fā)的好處就是不需要像串行一樣按照順序執(zhí)行,并發(fā)執(zhí)行可以顯著地提高速度。

三、dispatch_group_async的使用

有時候,我們會遇到這樣的情況,UI界面部分的顯示,需要在完成幾個任務(wù)再進行主任務(wù),例如3張圖片下載完畢,才通知UI界面已經(jīng)完成任務(wù)。

我們可以通過分派組(dispatch group)進行并發(fā)程序塊分配的運用,將異步分派(dispatch_async)的所有程序塊設(shè)置為松散,或者分配給多個線程來執(zhí)行,監(jiān)聽到這組任務(wù)全部完成后,使用dispatch_group_notify()通知并調(diào)用notify中的塊,例如UI界面的程序塊。

代碼:

- (IBAction)groupQueue:(UIButton *)sender {

dispatch_queue_t queue

=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_group_t group=dispatch_group_create();

dispatch_group_async(group, queue,^{

sleep(3);

NSLog(@"A任務(wù)");

});

dispatch_group_async(group, queue,^{

sleep(2);

NSLog(@"B任務(wù)");

});

//group組中的任務(wù)完后,通知并調(diào)用notify中的塊

dispatch_group_notify(group, queue, ^{

NSLog(@"主任務(wù)");

});

}

console控制臺顯示如下:

2016-03-16 11:18:41.306 dispatch_queue的多任務(wù)GCD使用[94865:2718342] B任務(wù)

2016-03-16 11:18:42.302 dispatch_queue的多任務(wù)GCD使用[94865:2718341] A任務(wù)

2016-03-16 11:18:42.303 dispatch_queue的多任務(wù)GCD使用[94865:2718341] 主任務(wù)

結(jié)果驗證了前面說的,直到分派組任務(wù)都完后,notify添加的任務(wù)塊才會執(zhí)行。

眼尖的讀者可能也發(fā)現(xiàn),整個任務(wù)組完成的時間比2個任務(wù)分別運行的時間還要短!這得益于我們同時進行了兩種計算~

當然在真實的開發(fā)運用中,這種明顯運行時間縮短的效果,取決于所需要執(zhí)行的工作量和可用的資源,以及多個CPU核心的可用性,因此在多核技術(shù)日益完善的大環(huán)境下,這樣一種多線程技術(shù)將得到更有效的利用。

四、dispatch_barrier_async的使用

dispatch_barrier(分派屏障)是當前面的任務(wù)執(zhí)行完后,才執(zhí)行barrier塊的任務(wù),而且后面的任務(wù)也得等到barrier塊的執(zhí)行完畢后才能開始執(zhí)行。

很好地突顯了“障礙物”這樣的特性,那么代碼上應(yīng)該怎么寫呢?

按照并發(fā)的性質(zhì),我們在barrierQueue方法中敲入以下代碼:

- (IBAction)barrierQueue:(UIButton *)sender {

dispatch_queue_t queue

=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_async(queue,^{

sleep(2);

NSLog(@"A任務(wù)");

});

dispatch_async(queue,^{

sleep(1);

NSLog(@"B任務(wù)");

});

dispatch_barrier_async(queue,^{

NSLog(@"barrier任務(wù)");

});

dispatch_async(queue,^{

sleep(1);

NSLog(@"C任務(wù)");

});

}

console控制臺顯示如下:

2016-03-16 13:18:47.525 dispatch_queue的多任務(wù)GCD使用[95191:2752854] barrier任務(wù)

2016-03-16 13:18:48.529 dispatch_queue的多任務(wù)GCD使用[95191:2752839] B任務(wù)

2016-03-16 13:18:48.529 dispatch_queue的多任務(wù)GCD使用[95191:2752844] C任務(wù)

2016-03-16 13:18:49.528 dispatch_queue的多任務(wù)GCD使用[95191:2752840] A任務(wù)

任務(wù)的執(zhí)行順序依然是跟并行隊列的方法一樣,barrier沒有發(fā)揮它的“障礙物”的界限作用。這是因為barrier這一塊是依賴隊列queue的模型來執(zhí)行的,當隊列為全局共享時,barrier就無法發(fā)揮其作用。我們需要新創(chuàng)建一個隊列,

dispatch_queue_t queue =?

dispatch_queue_create("zuoA", DISPATCH_QUEUE_SERIAL);

完整的程序如下:

- (IBAction)barrierQueue:(UIButton *)sender {

dispatch_queue_t queue=?

dispatch_queue_create("zuoA", DISPATCH_QUEUE_SERIAL);

dispatch_async(queue,^{

sleep(2);

NSLog(@"A任務(wù)");

});

dispatch_async(queue,^{

sleep(1);

NSLog(@"B任務(wù)");

});

dispatch_barrier_async(queue,^{

NSLog(@"barrier任務(wù)");

});

dispatch_async(queue,^{

sleep(1);

NSLog(@"C任務(wù)");

});

}

console控制臺顯示如下:

2016-03-16 13:30:14.251 dispatch_queue的多任務(wù)GCD使用[95263:2759658] A任務(wù)

2016-03-16 13:30:15.255 dispatch_queue的多任務(wù)GCD使用[95263:2759658] B任務(wù)

2016-03-16 13:30:15.255 dispatch_queue的多任務(wù)GCD使用[95263:2759658] barrier任務(wù)

2016-03-16 13:30:16.256 dispatch_queue的多任務(wù)GCD使用[95263:2759658] C任務(wù)

這就是我們想要得到的效果:確實只有在前面A、B任務(wù)完成后,barrier任務(wù)才能執(zhí)行,最后才能執(zhí)行C任務(wù)。

那么,dispatch_queue_create為什么要用DISPATCH_QUEUE_SERIAL,可以用其他么?答案是肯定的。把參數(shù)換成DISPATCH_QUEUE_CONCURRENT,

可以得到以下輸出:

2016-03-16 13:34:23.855 dispatch_queue的多任務(wù)GCD使用[95294:2762604] B任務(wù)

2016-03-16 13:34:24.853 dispatch_queue的多任務(wù)GCD使用[95294:2762603] A任務(wù)

2016-03-16 13:34:24.853 dispatch_queue的多任務(wù)GCD使用[95294:2762603] barrier任務(wù)

2016-03-16 13:34:25.856 dispatch_queue的多任務(wù)GCD使用[95294:2762603] C任務(wù)

也就是說,A、B、C任務(wù)完全是按照隊列的順序執(zhí)行,只是由于barrier塊的“屏障”作用,把A、B任務(wù)放在前面,而使得后來加入的C任務(wù)只有等到barrier塊執(zhí)行完畢才能運行;

五、dispatch_suspend(暫停)和 dispatch_resume(繼續(xù))

暫停:當需要暫停某個隊列queue時, 調(diào)用dispatch_suspend(queue),此時阻止了queue執(zhí)行塊對象,且queue的引用計數(shù)增加;

繼續(xù):繼續(xù)queue時,調(diào)用dispatch_resume(queue),此時queue啟動執(zhí)行塊的操作,queue的引用計數(shù)減少;

需要注意的是,suspend與resume是異步的,只在block塊之間調(diào)用,而且必須是成對存在的。

還有一些其他的dispatch函數(shù),例如

dispatch_once:可以使特定的塊在整個應(yīng)用程序生命周期中只被執(zhí)行一次~(在單例模式中使用到.)

dispatch_apply:執(zhí)行某個代碼片段n次(開發(fā)者可以自己設(shè)定)。

dispatch_after:當我們需要等待幾秒后進行某個操作,可以使用這個函數(shù);

注意事項:

1.在上面的例子中,我們沒有使用過手動內(nèi)管其內(nèi)存,因為系統(tǒng)會自動管理。

如果你部署的最低目標低于 iOS 6.0 or Mac OS X 10.8

就需要自己管理GCD對象,使用(dispatch_retain,dispatch_release),ARC并不會去管理它們

對于最低sdk版本>=ios6.0來說,GCD對象已經(jīng)納入了ARC的管理范圍,我們就不需要再手工調(diào)用 dispatch_release和dispatch_retain函數(shù)來增加和減少引用計數(shù)以管理內(nèi)存了。

2.之所以一直在說dispatch函數(shù),而不是方法,方法歸類、對象所有,是因為dispatch是C語言下的函數(shù),函數(shù)與方法是不同的,函數(shù)歸文件所有。

函數(shù)與方法有什么不同?看過一篇博文:OC中方法與函數(shù)的區(qū)別這個有興趣的童鞋可以了解一下。

本文旨在介紹dispatch_等函數(shù)的運用,文字篇幅有些大,敬請見諒(╯▽╰)



(轉(zhuǎn)載請標明原文出處,謝謝支持 ~ ^-^ ~)

? by:啊左~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • iOS中GCD的使用小結(jié) 作者dullgrass 2015.11.20 09:41*字數(shù) 4996閱讀 20199...
    DanDanC閱讀 1,295評論 0 0
  • 簡介 GCD(Grand Central Dispatch)是在macOS10.6提出來的,后來在iOS4.0被引...
    sunmumu1222閱讀 955評論 0 2
  • 本篇博客共分以下幾個模塊來介紹GCD的相關(guān)內(nèi)容: 多線程相關(guān)概念 多線程編程技術(shù)的優(yōu)缺點比較? GCD中的三種隊列...
    有夢想的老伯伯閱讀 1,090評論 0 4
  • 目錄(GCD): 關(guān)鍵詞 混淆點 場景應(yīng)用 總結(jié) 1. 關(guān)鍵詞 線程概念: 獨立執(zhí)行的代碼段,一個線程同時間只能執(zhí)...
    Ryan___閱讀 1,373評論 0 3
  • 工作日志 哥哥已經(jīng)有2天沒寫日志了!不是不想寫,只是不知道怎么寫! why? 因為周一周二的產(chǎn)出物為**讀書筆記*...
    錢佳辰_Hangzhou閱讀 248評論 0 0

友情鏈接更多精彩內(nèi)容