iOS - GCD 用法詳解

以前總是被并行、串行贮匕,同步姐仅、異步 隊列搞得暈頭轉(zhuǎn)向,最近理順了一遍GCD的用法,再也不迷路了掏膏。
一劳翰、Dispatch Queue 是什么?
執(zhí)行處理的等待隊列馒疹,簡單點(diǎn)說就是:要做一件事情先給開條路給做好準(zhǔn)備佳簸,做事情的時候直接處理事情。
Dispatch Queue 按照追加的順序(先進(jìn)先出 FIFO)執(zhí)行處理行冰。
二溺蕉、 Dispatch Queue 隊列種類
1、Serial Dispatch Queue 等待現(xiàn)在執(zhí)行中處理結(jié)束 等待執(zhí)行 (串行)
2悼做、Concurent Dispatch Queue 不等待現(xiàn)在執(zhí)行中處理結(jié)束 立即執(zhí)行 (并行)
三 疯特、創(chuàng)建一個 Serial Dispatch Queue 隊列,順序執(zhí)行任務(wù)肛走。

 NSLog(@"begin");
    //    Serila Disapatch Queue 串行隊列主要用在多個線程同事更新相同資源導(dǎo)致數(shù)據(jù)競爭時漓雅。
    //    第一個參數(shù)是進(jìn)程的標(biāo)識符 Apple 推薦寫法為 使用應(yīng)用程序ID這種逆序全程域名
    //    DISPATCH_QUEUE_SERIAL 表示隊列類型為 串行隊列  等待上個任務(wù)執(zhí)行完成 再往下執(zhí)行。
    dispatch_queue_t queue = dispatch_queue_create("queueSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"1");
    });
    dispatch_async(queue, ^{
        NSLog(@"2");
    });
    dispatch_async(queue, ^{
        NSLog(@"3");
    });
    dispatch_async(queue, ^{
        NSLog(@"4");
    });
    NSLog(@"end");

執(zhí)行順序為:

2018-04-26 10:45:33.304185+0800 GCD[1118:74823] begin
2018-04-26 10:45:33.304396+0800 GCD[1118:74823] end
2018-04-26 10:45:33.304408+0800 GCD[1118:74869] 1
2018-04-26 10:45:33.304573+0800 GCD[1118:74869] 2
2018-04-26 10:45:33.304710+0800 GCD[1118:74869] 3
2018-04-26 10:45:33.305090+0800 GCD[1118:74869] 4

結(jié)果很明顯: 開始后新建了一個子線程queue朽色, 主線程和子線程同時往下執(zhí)行邻吞,主線程執(zhí)行NSLog 輸出@“end”,子線程按照順序執(zhí)行任務(wù)葫男,輸出 1 2 3 4 抱冷。

四、創(chuàng)建一個 Concurrent Dispatch Queue 隊列梢褐,并發(fā)執(zhí)行任務(wù)旺遮。

     Concurrent Dispatch Queue  會立即執(zhí)行1 ,不等1執(zhí)行完 就開始執(zhí)行2 盈咳,不等2執(zhí)行完就開始執(zhí)行3耿眉,不等3執(zhí)行完再回到
    NSLog(@"begin");
    //    并行  第一個參數(shù) 是線程標(biāo)識符    第二個參數(shù)表示創(chuàng)建的是并行隊列 立即執(zhí)行
    dispatch_queue_t queue =dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"111111--- %d----%@", i, [NSThread currentThread]);
        };
    
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"222222--- %d----%@", i, [NSThread currentThread]);
        };
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"333333--- %d----%@", i, [NSThread currentThread]);
        };
    });
    NSLog(@"end");

結(jié)果為:

GCD[1489:125490] begin
GCD[1489:125490] end
GCD[1489:125581] 222222--- 0----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 0----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 0----<NSThread: 0x604000265580>{number = 5, name = (null)}
GCD[1489:125581] 222222--- 1----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 1----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 1----<NSThread: 0x604000265580>{number = 5, name = (null)}
GCD[1489:125581] 222222--- 2----<NSThread: 0x604000265480>{number = 4, name = (null)}
GCD[1489:125580] 111111--- 2----<NSThread: 0x604000265400>{number = 3, name = (null)}
GCD[1489:125578] 333333--- 2----<NSThread: 0x604000265580>{number = 5, name = (null)}

Concurrent Dispatch Queue 急性子 不管你有沒有做完前邊的事情,我反正要立即執(zhí)行鱼响,會開啟多個線程完成這個事情鸣剪。

這是最基本的兩種用法。

剛剛是自己創(chuàng)建Queue 丈积,其實我們也可以使用系統(tǒng)的Queue筐骇,

五、系統(tǒng)Serial (串行)隊列 dispatch_get_main_queue() 用法同三

 /**
     系統(tǒng)
     主隊列
     是Serial 類型
     */
    dispatch_queue_t mainQueue = dispatch_get_main_queue();

六桶癣、系統(tǒng)Concurrent(并行) 隊列 dispatch_get_global_queue(0, 0) 用法同四

   /**
     系統(tǒng)
     全局隊列
     是Concurrent 類型
     第一個參數(shù)是優(yōu)先級
     DISPATCH_QUEUE_PRIORITY_HIGH                  最高優(yōu)先
     DISPATCH_QUEUE_PRIORITY_DEFAULT            默認(rèn)優(yōu)先級  0
     DISPATCH_QUEUE_PRIORITY_LOW                    低
     DISPATCH_QUEUE_PRIORITY_BACKGROUND   后臺
     */

    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

還有一些其他函數(shù)例如 :
七拥褂、柵欄:dispatch_barrier_async()<用來控制隊列的執(zhí)行先完成barrier 前的任務(wù)在完成barrier后的任務(wù)>

//    柵欄函數(shù)不能在 全局主線程中執(zhí)行
//    柵欄函數(shù)不能在 全局主線程中執(zhí)行
    dispatch_queue_t queue = dispatch_queue_create("barrier", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"11111%d-----%@", i,[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"22222%d-----%@", i,[NSThread currentThread]);
        }
    });
        dispatch_barrier_async(queue, ^{
            NSLog(@"柵欄");
        });
    dispatch_async(queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"33333%d-----%@", i,[NSThread currentThread]);
        }
    });

輸出結(jié)果為:

GCD[2617:238165] 111110-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222220-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238165] 111111-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222221-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238165] 111112-----<NSThread: 0x60400027c100>{number = 3, name = (null)}
GCD[2617:238164] 222222-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 柵欄
GCD[2617:238164] 333330-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 333331-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}
GCD[2617:238164] 333332-----<NSThread: 0x60400027c1c0>{number = 4, name = (null)}

八、稍等:dispatch_after() <在子線程中過一段時間執(zhí)行某個任務(wù)>

 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"過2s后執(zhí)行");
    });

九牙寞、快速迭代:dispatch_apply() Demo 是from文件內(nèi)容轉(zhuǎn)移到to文件

 //    會阻塞主線程進(jìn)行
    NSLog(@"begin");
    NSString *from = @"/Users/youName/Desktop/from";
    NSString *to = @"/Users/youName/Desktop/to";
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *fileArray = [fileManager subpathsAtPath:from];
    /**
     第一個參數(shù) :迭代次數(shù)
     第二個參數(shù) :所在的queue
     第三個參數(shù) :執(zhí)行的任務(wù)
     */
    dispatch_apply([fileArray count], dispatch_queue_create("apply", DISPATCH_QUEUE_CONCURRENT), ^(size_t index) {
        sleep(2);
        NSLog(@"%zd ----%@",index, [NSThread currentThread]);
        NSString *fromPath = [from stringByAppendingPathComponent:fileArray[index]];
        NSString *toPath = [to stringByAppendingPathComponent:fileArray[index]];
        [fileManager moveItemAtPath:fromPath toPath:toPath error:nil];
    });
    NSLog(@"end");

輸出結(jié)果為:可以看到apply會阻塞主線程進(jìn)行饺鹃。

2018-04-26 13:33:43.071251+0800 GCD[2735:248799] begin
2018-04-26 13:33:45.073933+0800 GCD[2735:248896] 2 ----<NSThread: 0x600000275bc0>{number = 3, name = (null)}
2018-04-26 13:33:45.073933+0800 GCD[2735:248799] 0 ----<NSThread: 0x6000000634c0>{number = 1, name = main}
2018-04-26 13:33:45.073949+0800 GCD[2735:248895] 3 ----<NSThread: 0x604000263580>{number = 5, name = (null)}
2018-04-26 13:33:45.074026+0800 GCD[2735:248898] 1 ----<NSThread: 0x6040002634c0>{number = 4, name = (null)}
2018-04-26 13:33:47.077143+0800 GCD[2735:248799] 4 ----<NSThread: 0x6000000634c0>{number = 1, name = main}
2018-04-26 13:33:47.078235+0800 GCD[2735:248799] end

十莫秆、隊列組 dispatch_group_t group = dispatch_group_create();<保證所有的任務(wù)都已經(jīng)完成最后在執(zhí)行某個任務(wù)>

    //    demo 是兩張圖片下載完成后再合成一張圖片
    //    隊列組  是保證任務(wù)都已經(jīng)完成后在執(zhí)行某個任務(wù)。
    dispatch_group_t group = dispatch_group_create(); //    創(chuàng)建隊列組
    __block UIImage *image1 = [[UIImage alloc] init];
    __block  UIImage *image2 = [[UIImage alloc] init];
    UIImageView *imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height)];
    imageView.contentMode = UIViewContentModeScaleAspectFill;
    [self.view addSubview:imageView];
    NSLog(@"begin");
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSString *imagePath =@"http://h.hiphotos.baidu.com/image/pic/item/63d0f703918fa0ecf70575602a9759ee3c6ddb99.jpg";
        image1 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
        NSLog(@"任務(wù)1完成");
    });
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        NSString *imagePath =@"http://d.hiphotos.baidu.com/image/pic/item/8435e5dde71190ef3ddc94b7c21b9d16fdfa60b6.jpg";
        image2 = [UIImage imageWithData:[NSData dataWithContentsOfURL:[NSURL URLWithString:imagePath]]];
        NSLog(@"任務(wù)2完成");
    });
    //    所有任務(wù)執(zhí)行完成后才會執(zhí)行這個任務(wù)悔详。
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"所有任務(wù)執(zhí)行完畢開始最后合成圖片操作");
        //        拼接兩張圖片
        //        開啟圖形上下文
        UIGraphicsBeginImageContext(CGSizeMake([UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height));
        //        畫1
        [image1 drawInRect:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.width * 0.4)];
        //        畫2
        [image2 drawInRect:CGRectMake(0, [UIScreen mainScreen].bounds.size.height *0.3, [UIScreen mainScreen].bounds.size.width, [UIScreen mainScreen].bounds.size.height * 0.7)];
        //        得到繪制好的圖片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        //        關(guān)閉圖形上下文
        //        回到主線程刷新UI
        NSLog(@"合成完畢");
        dispatch_async(dispatch_get_main_queue(), ^{
            imageView.image = image;
            NSLog(@"回到主線程刷新UI");
        });
    });
    
    NSLog(@"end");

輸出結(jié)果為:可以看到group會等任務(wù)都完成后再做最后的操作镊屎。

2018-04-26 13:49:46.539181+0800 GCD[2910:273617] begin
2018-04-26 13:49:46.539414+0800 GCD[2910:273617] end
2018-04-26 13:49:48.905053+0800 GCD[2910:273683] 任務(wù)2完成
2018-04-26 13:49:48.905053+0800 GCD[2910:273679] 任務(wù)1完成
2018-04-26 13:49:48.905325+0800 GCD[2910:273617] 所有任務(wù)執(zhí)行完畢開始最后合成圖片操作
2018-04-26 13:49:48.941813+0800 GCD[2910:273617] 合成完畢
2018-04-26 13:49:48.942956+0800 GCD[2910:273617] 回到主線程刷新UI

十一、單例 dispatch_once()

//    在touchBegin 調(diào)用此方法
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"只會執(zhí)行一次  適合全局單例對象使用");
    });
    NSLog(@"別點(diǎn)了茄螃,就會執(zhí)行一次");

輸出結(jié)果為:

2018-04-26 13:53:42.167590+0800 GCD[2974:280429] 只會執(zhí)行一次  適合全局單例對象使用
2018-04-26 13:53:42.167835+0800 GCD[2974:280429] 別點(diǎn)了缝驳,就會執(zhí)行一次
2018-04-26 13:53:42.523634+0800 GCD[2974:280429] 別點(diǎn)了,就會執(zhí)行一次
2018-04-26 13:53:42.685084+0800 GCD[2974:280429] 別點(diǎn)了归苍,就會執(zhí)行一次

還有一些其他的用用法我還沒掌握用狱,歡迎各位大牛批評指正。

Demo 地址:https://github.com/MYLILUYANG/GCD-

更加詳細(xì)總結(jié): http://www.cocoachina.com/ios/20180313/22573.html

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拼弃,一起剝皮案震驚了整個濱河市夏伊,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌吻氧,老刑警劉巖溺忧,帶你破解...
    沈念sama閱讀 221,888評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異盯孙,居然都是意外死亡鲁森,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,677評論 3 399
  • 文/潘曉璐 我一進(jìn)店門振惰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來歌溉,“玉大人,你說我怎么就攤上這事骑晶⊙械祝” “怎么了?”我有些...
    開封第一講書人閱讀 168,386評論 0 360
  • 文/不壞的土叔 我叫張陵透罢,是天一觀的道長。 經(jīng)常有香客問我冠蒋,道長羽圃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,726評論 1 297
  • 正文 為了忘掉前任抖剿,我火速辦了婚禮朽寞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘斩郎。我一直安慰自己脑融,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,729評論 6 397
  • 文/花漫 我一把揭開白布缩宜。 她就那樣靜靜地躺著肘迎,像睡著了一般甥温。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上妓布,一...
    開封第一講書人閱讀 52,337評論 1 310
  • 那天姻蚓,我揣著相機(jī)與錄音,去河邊找鬼匣沼。 笑死狰挡,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的释涛。 我是一名探鬼主播加叁,決...
    沈念sama閱讀 40,902評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼唇撬!你這毒婦竟也來了它匕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,807評論 0 276
  • 序言:老撾萬榮一對情侶失蹤局荚,失蹤者是張志新(化名)和其女友劉穎超凳,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體耀态,經(jīng)...
    沈念sama閱讀 46,349評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡轮傍,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,439評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了首装。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片创夜。...
    茶點(diǎn)故事閱讀 40,567評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖仙逻,靈堂內(nèi)的尸體忽然破棺而出驰吓,到底是詐尸還是另有隱情,我是刑警寧澤系奉,帶...
    沈念sama閱讀 36,242評論 5 350
  • 正文 年R本政府宣布檬贰,位于F島的核電站,受9級特大地震影響缺亮,放射性物質(zhì)發(fā)生泄漏翁涤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,933評論 3 334
  • 文/蒙蒙 一萌踱、第九天 我趴在偏房一處隱蔽的房頂上張望葵礼。 院中可真熱鬧,春花似錦并鸵、人聲如沸鸳粉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,420評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽届谈。三九已至枯夜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間疼约,已是汗流浹背卤档。 一陣腳步聲響...
    開封第一講書人閱讀 33,531評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留程剥,地道東北人劝枣。 一個月前我還...
    沈念sama閱讀 48,995評論 3 377
  • 正文 我出身青樓,卻偏偏與公主長得像织鲸,于是被迫代替她去往敵國和親舔腾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,585評論 2 359