GCD 函數(shù)
// ===============================================================
// 異步后臺執(zhí)行
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
// do something
});
// ===============================================================
// 異步主線程執(zhí)行
dispatch_async(dispatch_get_main_queue(), ^{
// do something
});
// ===============================================================
// 一次性執(zhí)行:單例
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// code to be executed once
});
// ===============================================================
// 延遲2秒執(zhí)行
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
// code to be executed on the main queue after delay
});
// ===============================================================
// 使用 dispatch_queue_create() 自定義 dispatch_queue_t,
// 第一個參數(shù)是反向DNS樣式命名慣例;
// 第二個參數(shù)指定你的隊列是串行還是并發(fā);
// 注意:當你在網(wǎng)上搜索例子時蹋凝,你會經橙看人們傳遞 0 或者 NULL 給 dispatch_queue_create 的第二個參數(shù)粘勒。這是一個創(chuàng)建串行隊列的過時方式;明確你的參數(shù)總是更好。
dispatch_queue_t urls_quene = dispatch_queue_create("com.selander.GooglyPuff.photoQueue", NULL);
dispatch_async(urls_quene, ^{
// your code
});
// ===============================================================
// 并行執(zhí)行線程
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
// 并行執(zhí)行的線程一
});
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
// 并行執(zhí)行的線程二
});
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
// 匯總結果
});
// ===============================================================
// 每秒執(zhí)行
-(void)startTime{
__block int timeout = 60; // 倒計時時間
NSTimeInterval intervalInSeconds = 1.0; // 執(zhí)行時間間隔
//獲取后臺隊列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 以下代碼可以使用代碼片段庫: dispatch_source_t timer
// 創(chuàng)建一個timer放到隊列中
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
// 設置timer的首次執(zhí)行時間酷鸦、執(zhí)行時間間隔、精確度
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, intervalInSeconds * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
// 設置timer執(zhí)行的事件
if (timeout <= 0) {
dispatch_source_cancel(timer); // 關閉timer
dispatch_async(dispatch_get_main_queue(), ^{
// 倒計時結束,回到主線程更新UI
});
}else {
dispatch_async(dispatch_get_main_queue(), ^{
// 倒計時進行中机隙,更新UI
NSLog(@"===%ds===",timeout);
});
}
timeout --;
});
// 激活timer
dispatch_resume(timer);
}
隊列的四個優(yōu)先級
* - DISPATCH_QUEUE_PRIORITY_HIGH: QOS_CLASS_USER_INITIATED
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: QOS_CLASS_DEFAULT
* - DISPATCH_QUEUE_PRIORITY_LOW: QOS_CLASS_UTILITY
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND: QOS_CLASS_BACKGROUND
引用
下面是一個關于在 dispatch_async
上如何以及何時使用不同的隊列類型的快速指導:
- 自定義串行隊列:當你想串行執(zhí)行后臺任務并追蹤它時就是一個好選擇。這消除了資源爭用萨西,因為你知道一次只有一個任務在執(zhí)行有鹿。注意若你需要來自某個方法的數(shù)據(jù),你必須內聯(lián)另一個 Block 來找回它或考慮使用
dispatch_sync
谎脯。 - 主隊列(串行):這是在一個并發(fā)隊列上完成任務后更新 UI 的共同選擇葱跋。要這樣做,你將在一個 Block 內部編寫另一個 Block 。以及娱俺,如果你在主隊列調用
dispatch_async
到主隊列稍味,你能確保這個新任務將在當前方法完成后的某個時間執(zhí)行。 - 并發(fā)隊列:這是在后臺執(zhí)行非 UI 工作的共同選擇荠卷。
不知道何時適合使用 dispatch_after
模庐?
- 自定義串行隊列:在一個自定義串行隊列上使用
dispatch_after
要小心。你最好堅持使用主隊列油宜。 - 主隊列(串行):是使用
dispatch_after
的好選擇掂碱;Xcode 提供了一個不錯的自動完成模版。 - 并發(fā)隊列:在并發(fā)隊列上使用
dispatch_after
也要小心慎冤;你會這樣做就比較罕見疼燥。還是在主隊列做這些操作吧。
下面是你何時會——和不會——使用障礙函數(shù) Dispatch barriers
的情況:
- 自定義串行隊列:一個很壞的選擇粪薛;障礙不會有任何幫助悴了,因為不管怎樣,一個串行隊列一次都只執(zhí)行一個操作违寿。
- 全局并發(fā)隊列:要小心湃交;這可能不是最好的主意,因為其它系統(tǒng)可能在使用隊列而且你不能壟斷它們只為你自己的目的藤巢。
- 自定義并發(fā)隊列:這對于原子或臨界區(qū)代碼來說是極佳的選擇搞莺。任何你在設置或實例化的需要線程安全的事物都是使用障礙的最佳候選。
下面是一個快速總覽掂咒,關于在何時以及何處使用 dispatch_sync
:
- 自定義串行隊列:在這個狀況下要非常小心才沧!如果你正運行在一個隊列并調用
dispatch_sync
放在同一個隊列,那你就百分百地創(chuàng)建了一個死鎖绍刮。 - 主隊列(串行):同上面的理由一樣温圆,必須非常小心!這個狀況同樣有潛在的導致死鎖的情況孩革。
- 并發(fā)隊列:這才是做同步工作的好選擇岁歉,不論是通過調度障礙,或者需要等待一個任務完成才能執(zhí)行進一步處理的情況膝蜈。
Dispatch Groups
關于何時以及怎樣使用有著不同的隊列類型的 Dispatch Group :
- 自定義串行隊列:它很適合當一組任務完成時發(fā)出通知锅移。
- 主隊列(串行):它也很適合這樣的情況。但如果你要同步地等待所有工作地完成饱搏,那你就不應該使用它非剃,因為你不能阻塞主線程。然而推沸,異步模型是一個很有吸引力的能用于在幾個較長任務(例如網(wǎng)絡調用)完成后更新 UI 的方式备绽。
- 并發(fā)隊列:它也很適合 Dispatch Group 和完成時通知券坞。
① dispatch_group_wait
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
// 因為你在使用的是同步的 dispatch_group_wait ,它會阻塞當前線程疯坤,所以你要用 dispatch_async 將整個方法放入后臺隊列以避免阻塞主線程报慕。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
__block NSError *error;
// 創(chuàng)建一個新的 Dispatch Group,它的作用就像一個用于未完成任務的計數(shù)器压怠。
dispatch_group_t downloadGroup = dispatch_group_create(); // 2
for (NSInteger i = 0; i < 3; i++) {
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
// dispatch_group_enter 手動通知 Dispatch Group 任務已經開始眠冈。你必須保證 dispatch_group_enter 和 dispatch_group_leave 成對出現(xiàn),否則你可能會遇到詭異的崩潰問題菌瘫。
dispatch_group_enter(downloadGroup); // 3
Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
// 手動通知 Group 它的工作已經完成蜗顽。再次說明,你必須要確保進入 Group 的次數(shù)和離開 Group 的次數(shù)相等雨让。
dispatch_group_leave(downloadGroup); // 4
}];
[[PhotoManager sharedManager] addPhoto:photo];
}
// dispatch_group_wait 會一直等待雇盖,直到任務全部完成或者超時。如果在所有任務完成前超時了栖忠,該函數(shù)會返回一個非零值崔挖。你可以對此返回值做條件判斷以確定是否超出等待周期;然而庵寞,你在這里用 DISPATCH_TIME_FOREVER 讓它永遠等待狸相。它的意思,勿庸置疑就是捐川,永-遠-等-待脓鹃!這樣很好,因為圖片的創(chuàng)建工作總是會完成的古沥。
dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5
// 此時此刻瘸右,你已經確保了,要么所有的圖片任務都已完成岩齿,要么發(fā)生了超時太颤。然后,你在主線程上運行 completionBlock 回調盹沈。這會將工作放到主線程上栋齿,并在稍后執(zhí)行。
dispatch_async(dispatch_get_main_queue(), ^{ // 6
// 最后襟诸,檢查 completionBlock 是否為 nil,如果不是基协,那就運行它歌亲。
if (completionBlock) { // 7
completionBlock(error);
}
});
});
}
② dispatch_group_notify
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
// 1
// 在新的實現(xiàn)里,因為你沒有阻塞主線程澜驮,所以你并不需要將方法包裹在 async 調用中陷揪。
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();
for (NSInteger i = 0; i < 3; i++) {
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
// 同樣的 enter 方法,沒做任何修改。
dispatch_group_enter(downloadGroup); // 2
Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
// 同樣的 leave 方法悍缠,也沒做任何修改卦绣。
dispatch_group_leave(downloadGroup); // 3
}];
[[PhotoManager sharedManager] addPhoto:photo];
}
// dispatch_group_notify 以異步的方式工作。當 Dispatch Group 中沒有任何任務時飞蚓,它就會執(zhí)行其代碼滤港,那么 completionBlock 便會運行。你還指定了運行 completionBlock 的隊列趴拧,此處溅漾,主隊列就是你所需要的。
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4
if (completionBlock) {
completionBlock(error);
}
});
}
那何時才適合用 dispatch_apply
呢著榴?
- 自定義串行隊列:串行隊列會完全抵消
dispatch_apply
的功能添履;你還不如直接使用普通的for
循環(huán)。 - 主隊列(串行):與上面一樣脑又,在串行隊列上不適合使用
dispatch_apply
暮胧。還是用普通的for
循環(huán)吧。 - 并發(fā)隊列:對于并發(fā)循環(huán)來說是很好選擇问麸,特別是當你需要追蹤任務的進度時往衷。
- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
__block NSError *error;
dispatch_group_t downloadGroup = dispatch_group_create();
dispatch_apply(3, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {
NSURL *url;
switch (i) {
case 0:
url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
break;
case 1:
url = [NSURL URLWithString:kSuccessKidURLString];
break;
case 2:
url = [NSURL URLWithString:kLotsOfFacesURLString];
break;
default:
break;
}
dispatch_group_enter(downloadGroup);
Photo *photo = [[Photo alloc] initwithURL:url
withCompletionBlock:^(UIImage *image, NSError *_error) {
if (_error) {
error = _error;
}
dispatch_group_leave(downloadGroup);
}];
[[PhotoManager sharedManager] addPhoto:photo];
});
dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
if (completionBlock) {
completionBlock(error);
}
});
}