iOS 編程:GCD

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);
        }
    });
}

參考

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市口叙,隨后出現(xiàn)的幾起案子炼绘,更是在濱河造成了極大的恐慌,老刑警劉巖妄田,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件俺亮,死亡現(xiàn)場離奇詭異,居然都是意外死亡疟呐,警方通過查閱死者的電腦和手機脚曾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來启具,“玉大人本讥,你說我怎么就攤上這事÷撤耄” “怎么了拷沸?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長薯演。 經常有香客問我撞芍,道長,這世上最難降的妖魔是什么跨扮? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任序无,我火速辦了婚禮验毡,結果婚禮上,老公的妹妹穿的比我還像新娘帝嗡。我一直安慰自己晶通,他們只是感情好,可當我...
    茶點故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布哟玷。 她就那樣靜靜地躺著狮辽,像睡著了一般。 火紅的嫁衣襯著肌膚如雪碗降。 梳的紋絲不亂的頭發(fā)上隘竭,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天,我揣著相機與錄音讼渊,去河邊找鬼动看。 笑死,一個胖子當著我的面吹牛爪幻,可吹牛的內容都是我干的菱皆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼挨稿,長吁一口氣:“原來是場噩夢啊……” “哼仇轻!你這毒婦竟也來了?” 一聲冷哼從身側響起奶甘,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤篷店,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后臭家,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疲陕,經...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年钉赁,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹄殃。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,617評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡你踩,死狀恐怖诅岩,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情带膜,我是刑警寧澤吩谦,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站膝藕,受9級特大地震影響逮京,放射性物質發(fā)生泄漏。R本人自食惡果不足惜束莫,卻給世界環(huán)境...
    茶點故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一懒棉、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧览绿,春花似錦策严、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至怀各,卻和暖如春倔韭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背瓢对。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工寿酌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人硕蛹。 一個月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓醇疼,卻偏偏與公主長得像,于是被迫代替她去往敵國和親法焰。 傳聞我的和親對象是個殘疾皇子秧荆,可洞房花燭夜當晚...
    茶點故事閱讀 43,486評論 2 348

推薦閱讀更多精彩內容