本篇博客共分以下幾個模塊來介紹GCD的相關(guān)內(nèi)容:
- 多線程相關(guān)概念
- 多線程編程技術(shù)的優(yōu)缺點比較?
- GCD中的三種隊列類型
- The main queue(主線程串行隊列)
- Global queue(全局并發(fā)隊列)
- Custom queue (自定義隊列)
- Group queue (隊列組)
- GCD中一些系統(tǒng)提供的常用dispatch方法
歡迎訪問作者個人博客www.dullgrass.com,更多好的文章與您分享
多線程相關(guān)概念
- 進程與線程
- 進程概念: 進程是程序在計算機上的一次執(zhí)行活動,打開一個app,就開啟了一個進程,可包含多個線程。
- 線程概念: 獨立執(zhí)行的代碼段,一個線程同時間只能執(zhí)行一個任務(wù),反之多線程并發(fā)就可以在同一時間執(zhí)行多個任務(wù)。
- iOS程序中,主線程(又叫作UI線程)主要任務(wù)是處理UI事件,顯示和刷新UI,(只有主線程有直接修改UI的能力)耗時的操作放在子線程(又叫作后臺線程、異步線程)。在iOS中開子線程去處理耗時的操作,可以有效提高程序的執(zhí)行效率,提高資源利用率。但是開啟線程會占用一定的內(nèi)存,(主線程的堆棧大小是1M,第二個線程開始都是512KB,并且該值不能通過編譯器開關(guān)或線程API函數(shù)來更改)降低程序的性能。所以一般不要同時開很多線程。
- 線程相關(guān)
- 同步線程:同步線程會阻塞當(dāng)前線程去執(zhí)行線程內(nèi)的任務(wù),執(zhí)行完之后才會反回當(dāng)前線程。
- 異步線程:異步線程不會阻塞當(dāng)前線程,會開啟其他線程去執(zhí)行線程內(nèi)的任務(wù)。
- 串行隊列:線程任務(wù)按先后順序逐個執(zhí)行(需要等待隊列里面前面的任務(wù)執(zhí)行完之后再執(zhí)行新的任務(wù))。
- 并發(fā)隊列:多個任務(wù)按添加順序一起開始執(zhí)行(不用等待前面的任務(wù)執(zhí)行完再執(zhí)行新的任務(wù)),但是添加間隔往往忽略不計,所以看著像是一起執(zhí)行的。
- 并發(fā)VS并行:并行是基于多核設(shè)備的,并行一定是并發(fā),并發(fā)不一定是并行。
- 多線程中會出現(xiàn)的問題
- Critical Section(臨界代碼段)
指的是不能同時被兩個線程訪問的代碼段,比如一個變量,被并發(fā)進程訪問后可能會改變變量值,造成數(shù)據(jù)污染(數(shù)據(jù)共享問題)。 - Race Condition (競態(tài)條件)
當(dāng)多個線程同時訪問共享的數(shù)據(jù)時,會發(fā)生爭用情形,第一個線程讀取改變了一個變量的值,第二個線程也讀取改變了這個變量的值,兩個線程同時操作了該變量,此時他們會發(fā)生競爭來看哪個線程會最后寫入這個變量,最后被寫入的值將會被保留下來。 - Deadlock (死鎖)
兩個(多個)線程都要等待對方完成某個操作才能進行下一步,這時就會發(fā)生死鎖。 - Thread Safe(線程安全)
一段線程安全的代碼(對象),可以同時被多個線程或并發(fā)的任務(wù)調(diào)度,不會產(chǎn)生問題,非線程安全的只能按次序被訪問。- 所有Mutable對象都是非線程安全的,所有Immutable對象都是線程安全的,使用Mutable對象,一定要用同步鎖來同步訪問(@synchronized)。
- 互斥鎖:能夠防止多線程搶奪造成的數(shù)據(jù)安全問題,但是需要消耗大量的資源
- 原子屬性(atomic)加鎖
- atomic: 原子屬性,為setter方法加鎖,將屬性以atomic的形式來聲明,該屬性變量就能支持互斥鎖了。
- nonatomic: 非原子屬性,不會為setter方法加鎖,聲明為該屬性的變量,客戶端應(yīng)盡量避免多線程爭奪同一資源。
- Context Switch (上下文切換)
當(dāng)一個進程中有多個線程來回切換時,context switch用來記錄執(zhí)行狀態(tài),這樣的進程和一般的多線程進程沒有太大差別,但會產(chǎn)生一些額外的開銷。
多線程編程技術(shù)的優(yōu)缺點比較
- NSThread (抽象層次:低)
- 優(yōu)點:輕量級,簡單易用,可以直接操作線程對象
- 缺點: 需要自己管理線程的生命周期,線程同步。線程同步對數(shù)據(jù)的加鎖會有一定的系統(tǒng)開銷。
- Cocoa NSOperation (抽象層次:中)
- 優(yōu)點:不需要關(guān)心線程管理,數(shù)據(jù)同步的事情,可以把精力放在學(xué)要執(zhí)行的操作上。基于GCD,是對GCD 的封裝,比GCD更加面向?qū)ο?/li>
- 缺點: NSOperation是個抽象類,使用它必須使用它的子類,可以實現(xiàn)它或者使用它定義好的兩個子類NSInvocationOperation、NSBlockOperation.
- GCD 全稱Grand Center Dispatch (抽象層次:高)
優(yōu)點:是 Apple 開發(fā)的一個多核編程的解決方法,簡單易用,效率高,速度快,基于C語言,更底層更高效,并且不是Cocoa框架的一部分,自動管理線程生命周期(創(chuàng)建線程、調(diào)度任務(wù)、銷毀線程)。
-
缺點: 使用GCD的場景如果很復(fù)雜,就有非常大的可能遇到死鎖問題。
GCD抽象層次最高,使用也簡單,因此,蘋果也推薦使用GCD
GCD中的三種隊列類型
<p style = "text-indent:2em;font-size = 20px;">GCD編程的核心就是dispatch隊列,dispatch block的執(zhí)行最終都會放進某個隊列中去進行。</p>
- The main queue(主線程串行隊列): 與主線程功能相同,提交至Main queue的任務(wù)會在主線程中執(zhí)行,
- Main queue 可以通過dispatch_get_main_queue()來獲取。
- Global queue(全局并發(fā)隊列): 全局并發(fā)隊列由整個進程共享,有高、中(默認(rèn))、低、后臺四個優(yōu)先級別。
- Global queue 可以通過調(diào)用dispatch_get_global_queue函數(shù)來獲?。梢栽O(shè)置優(yōu)先級)
- Custom queue (自定義隊列): 可以為串行,也可以為并發(fā)。
- Custom queue 可以通過dispatch_queue_create()來獲??;
- Group queue (隊列組):將多線程進行分組,最大的好處是可獲知所有線程的完成情況。
- Group queue 可以通過調(diào)用dispatch_group_create()來獲取,通過dispatch_group_notify,可以直接監(jiān)聽組里所有線程完成情況。
<p style = "text-indent:2em;font-size =14 px;">gcd中相關(guān)函數(shù)的使用一般都是以dispatch開頭</p>
The main queue(主線程串行隊列)
<p style = "text-indent:2em;font-size =14 px;">dispatch_sync 同步執(zhí)行任務(wù)函數(shù),不會開啟新的線程,dispatch_async 異步執(zhí)行任務(wù)函數(shù),會開啟新的線程</p>
- 獲取主線程串行隊列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
- 主線程串行隊列同步執(zhí)行任務(wù),在主線程運行時,會產(chǎn)生死鎖
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_sync(mainQueue,^{
NSLog("MainQueue");
});
程序一直處于等待狀態(tài),block中的代碼將執(zhí)行不到
- 主線程串行隊列異步執(zhí)行任務(wù),在主線程運行,不會產(chǎn)生死鎖。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue,^{
NSLog("MainQueue");
});
程序正常運行,block中的代碼正常運行
- 從子線程,異步返回主線程更新UI<這種使用方式比較多>
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
//子線程異步執(zhí)行下載任務(wù),防止主線程卡頓
NSURL *url = [NSURL URLWithString:@"http://www.baidu.com"];
NSError *error;
NSString *htmlData = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:&error];
if (htmlData != nil) {
dispatch_queue_t mainQueue = dispatch_get_main_queue();
//異步返回主線程,根據(jù)獲取的數(shù)據(jù),更新UI
dispatch_async(mainQueue, ^{
NSLog(@"根據(jù)更新UI界面");
});
} else {
NSLog(@"error when download:%@",error);
}
});
主線程串行隊列由系統(tǒng)默認(rèn)生成的,所以無法調(diào)用dispatch_resume()和dispatch_suspend()來控制執(zhí)行繼續(xù)或中斷。
Global queue(全局并發(fā)隊列)
<p style = "text-indent:2em;font-size =14 px;">耗時的操作,比如讀取網(wǎng)絡(luò)數(shù)據(jù),IO,數(shù)據(jù)庫讀寫等,我們會在另外一個線程中處理這些操作,然后通知主線程更新界面</p>
- 獲取全局并發(fā)隊列
//程序默認(rèn)的隊列級別,一般不要修改,DISPATCH_QUEUE_PRIORITY_DEFAULT == 0
dispatch_queue_t globalQueue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//HIGH
dispatch_queue_t globalQueue2 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
//LOW
dispatch_queue_t globalQueue3 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);
//BACKGROUND
dispatch_queue_t globalQueue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);
- 全局并發(fā)隊列同步執(zhí)行任務(wù),在主線程執(zhí)行會導(dǎo)致頁面卡頓。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_sync(globalQueue, ^{
sleep(2.0);
NSLog(@"sleep 2.0s");
});
NSLog(@"next task");
控制臺輸出如下:
2015-11-18 15:51:45.550 Whisper[33152:345023] current task
2015-11-18 15:51:47.552 Whisper[33152:345023] sleep 2.0s
2015-11-18 15:51:47.552 Whisper[33152:345023] next task
2s鐘之后,才會執(zhí)行block代碼段下面的代碼。
- 全局并發(fā)隊列異步執(zhí)行任務(wù),在主線程運行,會開啟新的子線程去執(zhí)行任務(wù),頁面不會卡頓。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_async(globalQueue, ^{
sleep(2.0);
NSLog(@"sleep 2.0s");
});
NSLog(@"next task");
控制臺輸出如下:
2015-11-18 15:50:14.999 Whisper[33073:343781] current task
2015-11-18 15:50:15.000 Whisper[33073:343781] next task
2015-11-18 15:50:17.004 Whisper[33073:343841] sleep 2.0s
主線程不用等待2s鐘,繼續(xù)執(zhí)行block代碼段后面的代碼。
- 多個全局并發(fā)隊列,異步執(zhí)行任務(wù)。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_async(globalQueue, ^{
NSLog(@"最先加入全局并發(fā)隊列");
});
dispatch_async(globalQueue, ^{
NSLog(@"次加入全局并發(fā)隊列");
});
NSLog(@"next task");
控制臺輸出如下:
2015-11-18 16:54:52.202 Whisper[39827:403208] current task
2015-11-18 16:54:52.203 Whisper[39827:403208] next task
2015-11-18 16:54:52.205 Whisper[39827:403309] 最先加入全局并發(fā)隊列
2015-11-18 16:54:52.205 Whisper[39827:403291] 次加入全局并發(fā)隊列
異步線程的執(zhí)行順序是不確定的。幾乎同步開始執(zhí)行
全局并發(fā)隊列由系統(tǒng)默認(rèn)生成的,所以無法調(diào)用dispatch_resume()和dispatch_suspend()來控制執(zhí)行繼續(xù)或中斷。
Custom queue (自定義隊列)
- 自定義串行隊列
- 獲取自定義串行隊列
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"%s",dispatch_queue_get_label(conCurrentQueue)) ;
- 控制臺輸出:
2015-11-19 11:05:34.469 Whisper[1223:42960] com.dullgrass.serialQueue
<p style = "text-indent:2em;font-size = 20px;">dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)函數(shù)中第一個參數(shù)是給這個queue起的標(biāo)識,這個在調(diào)試的可以看到是哪個隊列在執(zhí)行,或者在crash日志中,也能做為提示。第二個是需要創(chuàng)建的隊列類型,是串行的還是并發(fā)的</p>
- 自定義串行隊列同步執(zhí)行任務(wù)
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
NSLog(@"current task");
dispatch_sync(serialQueue, ^{
NSLog(@"最先加入自定義串行隊列");
sleep(2);
});
dispatch_sync(serialQueue, ^{
NSLog(@"次加入自定義串行隊列");
});
NSLog(@"next task");
- 控制臺輸出:
2015-11-18 17:09:40.025 Whisper[40241:416296] current task
2015-11-18 17:09:40.027 Whisper[40241:416296] 最先加入自定義串行隊列
2015-11-18 17:09:43.027 Whisper[40241:416296] 次加入自定義串行隊列
2015-11-18 17:09:43.027 Whisper[40241:416296] next task
<p style = "text-indent:2em;font-size = 20px;">當(dāng)前線程等待串行隊列中的子線程執(zhí)行完成之后再執(zhí)行,串行隊列中先進來的子線程先執(zhí)行任務(wù),執(zhí)行完成后,再執(zhí)行隊列中后面的任務(wù)。</p>
- 自定義串行隊列嵌套執(zhí)行同步任務(wù),產(chǎn)生死鎖
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{ //該代碼段后面的代碼都不會執(zhí)行,程序被鎖定在這里
NSLog(@"會執(zhí)行的代碼");
dispatch_sync(serialQueue, ^{
NSLog(@"代碼不執(zhí)行");
});
});
- 異步執(zhí)行串行隊列,嵌套同步執(zhí)行串行隊列,同步執(zhí)行的串行隊列中的任務(wù)將不會被執(zhí)行,其他程序正常執(zhí)行
dispatch_queue_t serialQueue = dispatch_queue_create("com.dullgrass.serialQueue", DISPATCH_QUEUE_SERIAL);
dispatch_async(serialQueue, ^{
NSLog(@"會執(zhí)行的代碼");
dispatch_sync(serialQueue, ^{
NSLog(@"代碼不執(zhí)行");
});
});
<p style = "text-indent:2em;font-size =14 px;">注意不要嵌套使用同步執(zhí)行的串行隊列任務(wù)</p>
-
自定義并發(fā)隊列
- 獲取自定義并發(fā)隊列
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);- 自定義并發(fā)隊列執(zhí)行同步任務(wù)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"current task"); dispatch_sync(conCurrentQueue, ^{ NSLog(@"先加入隊列"); }); dispatch_sync(conCurrentQueue, ^{ NSLog(@"次加入隊列"); }); NSLog(@"next task");- 控制臺輸出如下:
2015-11-19 10:36:23.259 Whisper[827:20596] current task 2015-11-19 10:36:23.261 Whisper[827:20596] 先加入隊列 2015-11-19 10:36:23.261 Whisper[827:20596] 次加入隊列 2015-11-19 10:36:23.261 Whisper[827:20596] next task- 自定義并發(fā)隊列嵌套執(zhí)行同步任務(wù)(不會產(chǎn)生死鎖,程序正常運行)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"current task"); dispatch_sync(conCurrentQueue, ^{ NSLog(@"先加入隊列"); dispatch_sync(conCurrentQueue, ^{ NSLog(@"次加入隊列"); }); }); NSLog(@"next task");- 控制臺輸出如下:
2015-11-19 10:39:21.301 Whisper[898:22273] current task 2015-11-19 10:39:21.303 Whisper[898:22273] 先加入隊列 2015-11-19 10:39:21.303 Whisper[898:22273] 次加入隊列 2015-11-19 10:39:21.303 Whisper[898:22273] next task- 自定義并發(fā)隊列執(zhí)行異步任務(wù)
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT); NSLog(@"current task"); dispatch_async(conCurrentQueue, ^{ NSLog(@"先加入隊列"); }); dispatch_async(conCurrentQueue, ^{ NSLog(@"次加入隊列"); }); NSLog(@"next task");- 控制臺輸出如下:
2015-11-19 10:45:22.290 Whisper[1050:26445] current task 2015-11-19 10:45:22.290 Whisper[1050:26445] next task 2015-11-19 10:45:22.290 Whisper[1050:26505] 次加入隊列 2015-11-19 10:45:22.290 Whisper[1050:26500] 先加入隊列<p style = "text-indent:2em;font-size =14 px;">異步執(zhí)行任務(wù),開啟新的子線程,不影響當(dāng)前線程任務(wù)的執(zhí)行,并發(fā)隊列中的任務(wù),幾乎是同步執(zhí)行的,輸出順序不確定</p>
Group queue (隊列組)
<p style = "text-indent:2em;font-size = 20px;">當(dāng)遇到需要執(zhí)行多個線程并發(fā)執(zhí)行,然后等多個線程都結(jié)束之后,再匯總執(zhí)行結(jié)果時可以用group queue</p>
- 使用場景: 同時下載多個圖片,所有圖片下載完成之后去更新UI(需要回到主線程)或者去處理其他任務(wù)(可以是其他線程隊列)。
- 原理:使用函數(shù)dispatch_group_create創(chuàng)建dispatch group,然后使用函數(shù)dispatch_group_async來將要執(zhí)行的block任務(wù)提交到一個dispatch queue。同時將他們添加到一個組,等要執(zhí)行的block任務(wù)全部執(zhí)行完成之后,使用dispatch_group_notify函數(shù)接收完成時的消息。
- 使用示例:
dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_group_t groupQueue = dispatch_group_create();
NSLog(@"current task");
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
NSLog(@"并行任務(wù)1");
});
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
NSLog(@"并行任務(wù)2");
});
dispatch_group_notify(groupQueue, mainQueue, ^{
NSLog(@"groupQueue中的任務(wù) 都執(zhí)行完成,回到主線程更新UI");
});
NSLog(@"next task");
- 控制臺輸出:
2015-11-19 13:47:55.117 Whisper[1645:97116] current task
2015-11-19 13:47:55.117 Whisper[1645:97116] next task
2015-11-19 13:47:55.119 Whisper[1645:97178] 并行任務(wù)1
2015-11-19 13:47:55.119 Whisper[1645:97227] 并行任務(wù)2
2015-11-19 13:47:55.171 Whisper[1645:97116] groupQueue中的任務(wù) 都執(zhí)行完成,回到主線程更新UI
- 在當(dāng)前線程阻塞的同步等待dispatch_group_wait
dispatch_group_t groupQueue = dispatch_group_create();
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, 10 * NSEC_PER_SEC);
dispatch_queue_t conCurrentGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
NSLog(@"current task");
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
long isExecuteOver = dispatch_group_wait(groupQueue, delayTime);
if (isExecuteOver) {
NSLog(@"wait over");
} else {
NSLog(@"not over");
}
NSLog(@"并行任務(wù)1");
});
dispatch_group_async(groupQueue, conCurrentGlobalQueue, ^{
NSLog(@"并行任務(wù)2");
});
控制臺輸出如下:
2015-11-19 14:37:29.514 Whisper[2426:126683] current task
2015-11-19 14:37:29.518 Whisper[2426:126791] 并行任務(wù)2
2015-11-19 14:37:39.515 Whisper[2426:126733] wait over
2015-11-19 14:37:39.516 Whisper[2426:126733] 并行任務(wù)1
dispatch_time(dispatch_time_t when, int64_t delta);
參數(shù)注釋:
第一個參數(shù)一般是DISPATCH_TIME_NOW,表示從現(xiàn)在開始
第二個參數(shù)是延時的具體時間
延時1秒可以寫成如下幾種:
NSEC_PER_SEC----每秒有多少納秒
dispatch_time(DISPATCH_TIME_NOW, 1*NSEC_PER_SEC);
USEC_PER_SEC----每秒有多少毫秒(注意是指在納秒的基礎(chǔ)上)
dispatch_time(DISPATCH_TIME_NOW, 1000*USEC_PER_SEC); //SEC---毫秒
NSEC_PER_USEC----每毫秒有多少納秒。
dispatch_time(DISPATCH_TIME_NOW, USEC_PER_SEC*NSEC_PER_USEC);SEC---納秒
GCD中一些系統(tǒng)提供的常用dispatch方法
- dispatch_after延時添加到隊列
- 使用示例:
dispatch_time_t delayTime3 = dispatch_time(DISPATCH_TIME_NOW, 3*NSEC_PER_SEC);
dispatch_time_t delayTime2 = dispatch_time(DISPATCH_TIME_NOW, 2*NSEC_PER_SEC);
dispatch_queue_t mainQueue = dispatch_get_main_queue();
NSLog(@"current task");
dispatch_after(delayTime3, mainQueue, ^{
NSLog(@"3秒之后添加到隊列");
});
dispatch_after(delayTime2, mainQueue, ^{
NSLog(@"2秒之后添加到隊列");
});
NSLog(@"next task");
- 控制臺輸出如下:
2015-11-19 15:50:19.369 Whisper[2725:172593] current task
2015-11-19 15:50:19.370 Whisper[2725:172593] next task
2015-11-19 15:50:21.369 Whisper[2725:172593] 2秒之后添加到隊列
2015-11-19 15:50:22.654 Whisper[2725:172593] 3秒之后添加到隊列
<p style = "text-indent:2em;font-size = 20px;">dispatch_after只是延時提交block,并不是延時后立即執(zhí)行,并不能做到精確控制,需要精確控制的朋友慎用哦</p>
- dispatch_apply在給定的隊列上多次執(zhí)行某一任務(wù),在主線程直接調(diào)用會阻塞主線程去執(zhí)行block中的任務(wù)。
- dispatch_apply函數(shù)的功能:把一項任務(wù)提交到隊列中多次執(zhí)行,隊列可以是串行也可以是并行,dispatch_apply不會立刻返回,在執(zhí)行完block中的任務(wù)后才會返回,是同步執(zhí)行的函數(shù)。
- dispatch_apply正確使用方法:為了不阻塞主線程,一般把dispatch_apply放在異步隊列中調(diào)用,然后執(zhí)行完成后通知主線程
- 使用示例:
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
NSLog(@"current task");
dispatch_async(globalQueue, ^{
dispatch_queue_t applyQueue = dispatch_get_global_queue(0, 0);
//第一個參數(shù),3--block執(zhí)行的次數(shù)
//第二個參數(shù),applyQueue--block任務(wù)提交到的隊列
//第三個參數(shù),block--需要重復(fù)執(zhí)行的任務(wù)
dispatch_apply(3, applyQueue, ^(size_t index) {
NSLog(@"current index %@",@(index));
sleep(1);
});
NSLog(@"dispatch_apply 執(zhí)行完成");
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
NSLog(@"回到主線程更新UI");
});
});
NSLog(@"next task");
- 控制臺輸出如下:
2015-11-19 16:24:45.015 Whisper[4034:202269] current task
2015-11-19 16:24:45.016 Whisper[4034:202269] next task
2015-11-19 16:24:45.016 Whisper[4034:202347] current index 0
2015-11-19 16:24:45.016 Whisper[4034:202344] current index 1
2015-11-19 16:24:45.016 Whisper[4034:202345] current index 2
2015-11-19 16:24:46.021 Whisper[4034:202347] dispatch_apply 執(zhí)行完成
2015-11-19 16:24:46.021 Whisper[4034:202269] 回到主線程更新UI
- 嵌套使用dispatch_apply會導(dǎo)致死鎖。
- dispatch_once保證在app運行期間,block中的代碼只執(zhí)行一次
- 經(jīng)典使用場景---單例
- 單例對象ShareManager的定義:
ShareManager的.h文件
#import <Foundation/Foundation.h>
@interface ShareManager : NSObject
@property (nonatomic, copy) NSString *someProperty;
+ (ShareManager *)shareManager;
+ (ShareManager *)sharedManager;
@end
ShareManager的.m文件
#import "ShareManager.h"
@implementation ShareManager
static ShareManager *sharedManager = nil;
//GCD實現(xiàn)單例功能
+ (ShareManager *)shareManager
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedManager = [[self alloc] init];
});
return sharedManager;
}
//在ARC下,非GCD,實現(xiàn)單例功能
+ (ShareManager *)sharedManager
{
@synchronized(self) {
if (!sharedManager) {
sharedManager = [[self alloc] init];
}
}
return sharedManager;
}
- (instancetype)init{
self = [super init];
if (self) {
_someProperty =@"Default Property Value";
}
return self;
}
@end
ShareManager的使用
#import "ShareManager.h"
在需要使用的函數(shù)中,直接調(diào)用下面的方法
ShareManager *share = [ShareManager sharedManager];
NSLog(@"share is %@",share.someProperty);
- dispatch_barrier_async 柵欄的作用
- 功能:是在并行隊列中,等待在dispatch_barrier_async之前加入的隊列全部執(zhí)行完成之后(這些任務(wù)是并發(fā)執(zhí)行的)再執(zhí)行dispatch_barrier_async中的任務(wù),dispatch_barrier_async中的任務(wù)執(zhí)行完成之后,再去執(zhí)行在dispatch_barrier_async之后加入到隊列中的任務(wù)(這些任務(wù)是并發(fā)執(zhí)行的)。
- 使用示例:
dispatch_queue_t conCurrentQueue = dispatch_queue_create("com.dullgrass.conCurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(conCurrentQueue, ^{
NSLog(@"dispatch 1");
});
dispatch_async(conCurrentQueue, ^{
NSLog(@"dispatch 2");
});
dispatch_barrier_async(conCurrentQueue, ^{
NSLog(@"dispatch barrier");
});
dispatch_async(conCurrentQueue, ^{
NSLog(@"dispatch 3");
});
dispatch_async(conCurrentQueue, ^{
NSLog(@"dispatch 4");
});
- 控制臺輸出如下:
2015-11-19 18:12:34.125 Whisper[22633:297257] dispatch 1
2015-11-19 18:12:34.125 Whisper[22633:297258] dispatch 2
2015-11-19 18:12:34.126 Whisper[22633:297258] dispatch barrier
2015-11-19 18:12:34.127 Whisper[22633:297258] dispatch 3
2015-11-19 18:12:34.127 Whisper[22633:297257] dispatch 4