什么是 GCD
GCD(Grand Central Dispatch)是異步執(zhí)行任務(wù)的技術(shù)之一画饥,借助 GCD 可以用很簡潔的代碼就實現(xiàn)多線程編程扫倡,提高程序效率。如下
dispatch_async(queue, ^{
/*
* 處理耗時長的任務(wù)
* 比如文件下載渤弛,數(shù)據(jù)庫訪問
*/
/*
* 任務(wù)結(jié)束后巾乳,在主線程中處理結(jié)果
*/
dispatch_async(dispatch_get_main_queue(), ^{
/*
* 只在主線程可以執(zhí)行的處理
* 比如更新用戶界面
*/
});
});
什么是多線程編程?
多線程是指從軟件或者硬件上實現(xiàn)多個線程并發(fā)執(zhí)行的技術(shù)厂僧。即便處理器只能運行一個線程扣草,操作系統(tǒng)也可以通過快速的在不同線程之間進行切換,由于時間間隔很小颜屠,來給用戶造成一種多個線程同時運行的假象辰妙。
多線程容易發(fā)生問題:多個線程更新相同的資源會導(dǎo)致數(shù)據(jù)的不一致(數(shù)據(jù)競爭),停止等待事件的線程會導(dǎo)致多個線程相互持續(xù)等待(死鎖)甫窟、使用太多線程會消耗大量內(nèi)測密浑。如圖:
要處理回避這些問題會讓程序變得復(fù)雜。但多線程可保證應(yīng)用程序的響應(yīng)性能粗井。如果在主線程中長時間的處理尔破,比如下載大文件,會妨礙主線程的執(zhí)行背传。在 iOS 應(yīng)用程序中呆瞻,會妨礙主線程中 RunLoop 的主循環(huán)的執(zhí)行,從而導(dǎo)致不能刷新用戶界面径玖,應(yīng)用程序長時間卡頓等問題痴脾。
使用多線程編程,在執(zhí)行長時間的處理時仍可保證用戶界面的響應(yīng)性能梳星。GCD 極大簡化了復(fù)雜的多線程編程的源代碼赞赖。
GCD 的 API
Dispatch Queue
我們只需定義想執(zhí)行的任務(wù)并追加到適當(dāng)?shù)?Dispatch Queue 中。Dispatch Queue 是執(zhí)行處理的等待隊列冤灾。當(dāng)我們將需要執(zhí)行的處理追加到 Dispatch Queue 中前域,Dispatch Queue 會按照追加的順序(先進先出FIFO)執(zhí)行處理。
兩種 Dispatch Queue
- Serial Dispatch Queue韵吨,順序執(zhí)行任務(wù)匿垄,每次只執(zhí)行一個任務(wù)
- Concurrent Dispatch Queue,并發(fā)處理多個任務(wù)
Main Dispatch Queue/Global Dispatch Queue
通過調(diào)用系統(tǒng)的 Main Dispatch Queue 和 Global Dispatch Queue 可生成 Dispatch Queue归粉,如下:
名稱 | 種類 | 說明 |
---|---|---|
Main Dispatch Queue 3 | Serial Dispatch Queue | 主線程執(zhí)行 |
Global Dispatch Queue (High Priority) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級:高 |
Global Dispatch Queue (Default Priority) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級:默認 |
Global Dispatch Queue (Low Priority) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級:低 |
Global Dispatch Queue (Background Priority) | Concurrent Dispatch Queue | 執(zhí)行優(yōu)先級:后臺 |
代碼如下
// 在默認優(yōu)先級的 Global Dispatch Queue 中執(zhí)行 Block
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
/*
* 可并行執(zhí)行的處理
*/
dispatch_async(dispatch_get_main_queue(), ^{
/*
* 只在主線程可以執(zhí)行的處理
*/
});
});
dispatch_after
如果需要延遲處理任務(wù)時椿疗,可使用 dispatch_after 來實現(xiàn)。如下:
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, 3ull * NSEC_PER_SEC);
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"wait at least three seconds.");
});
需要注意的是糠悼,dispatch_after 函數(shù)并不是在指定時間后執(zhí)行届榄,而只是在指定時間追加到 dispatch queue。因為 Main Dispatch Queue 在主線程的 RunLoop 中執(zhí)行倔喂,如果 RunLoop 每隔 1/60 執(zhí)行铝条,則 Block 最快在 3 秒后執(zhí)行靖苇,最慢在 3 秒 +1/60 秒執(zhí)行。
Dispatch Group
如果想等待 Dispatch Queue 中的多個任務(wù)全部執(zhí)行結(jié)束后班缰,再處理其結(jié)果贤壁,可使用 Dispatch Group。如下:
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, ^{NSLog(@"blk0");});
dispatch_group_async(group, queue, ^{NSLog(@"blk1");});
dispatch_group_async(group, queue, ^{NSLog(@"blk2");});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"done");
});
NSLog(@"done");
會等待^{NSLog(@"blk0");}
^{NSLog(@"blk0");}
^{NSLog(@"blk0");}
全部執(zhí)行結(jié)束后再執(zhí)行鲁捏。在網(wǎng)絡(luò)訪問中經(jīng)常會需要等待多個請求返回再做進一步的處理芯砸,這個時候就可以用 Dispatch Group。
dispatch_once
dispatch_once 函數(shù)保證在應(yīng)用程序中只執(zhí)行一次指定的代碼给梅。創(chuàng)建單例時經(jīng)常使用:
+ (instancetype)manager {
static ApiManager *manager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
manager = [[ApiManager alloc] init];
});
return manager;
}