GCD你應(yīng)該知道的事兒

寫在最前邊咒彤,這邊文章適合剛?cè)胄谢蛘邔CD不是很了解的同學(xué)閱讀,大神請略過~~~~~~

為何要寫這篇文章呢咒精?

最近看了好多iOS簡歷镶柱,在專業(yè)技能上都會寫有這么一條** “熟悉NSThread、NSOperation模叙、GCD等多線程開發(fā)技術(shù)” **歇拆,看到這里我都會問同一個(gè)問題,就是 “你對其中哪種技術(shù)最熟悉范咨,或者用的最多”故觅,99%的面試者給出的答案都是GCD。進(jìn)一步詢問使用情況的時(shí)候渠啊,大多數(shù)面試者都會給出比較簡單的答案输吏。

dispatch_async(dispatch_get_global_queue(0, 0), ^{
//執(zhí)行耗時(shí)操作
……
dispatch_async(dispatch_get_main_queue(), ^{
//回到主線程進(jìn)行UI刷新操作
};
};

然后就基本沒有然后了。這篇文章我會針對GCD一些常用的方法做一下簡要分析(太復(fù)雜的方法我也不會~~~)替蛉,希望能對剛?cè)胄械耐瑢W(xué)們有一定的幫助贯溅。

串行 & 并行

GCD中dispatch_queue大致可以分為三類

  1. 全局的并行的queue
  2. 主線程的串行的queue
  3. 自定義的queue

全局的queue和主線程的queue結(jié)合使用(上邊提到的)就是我們平常最常用的一種用法,在異步線程中執(zhí)行耗時(shí)操作躲查,然后在UI線程執(zhí)行刷新操作它浅。
全局的queue我們可以通過dispatch_get_global_queue(0, 0)直接獲取,這里有兩個(gè)參數(shù)镣煮,第一個(gè)表示線程執(zhí)行的優(yōu)先級(第二個(gè)參數(shù)是預(yù)留參數(shù)暫時(shí)沒有什么鳥用)姐霍,什么意思呢?當(dāng)我們通過dispatch_async(globalQueue, ^{}); 這種方式去異步執(zhí)行一個(gè)操作時(shí)怎静,實(shí)際上操作系統(tǒng)會創(chuàng)建一個(gè)新的線程邮弹,當(dāng)我們同時(shí)執(zhí)行多個(gè)這樣的操作時(shí),我們?nèi)绾文鼙WC哪個(gè)線程先執(zhí)行呢蚓聘?這時(shí)候這個(gè)參數(shù)就派上了用場腌乡。這個(gè)參數(shù)一共可以有四個(gè)值:

#######define DISPATCH_QUEUE_PRIORITY_HIGH 2
#######define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#######define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#######define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

</code>
下面來看一段代碼,我們設(shè)置默認(rèn)值0

dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"start task 1");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 1");
});   
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"start task 2");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 2");
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"start task 3");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 3");
});
第一個(gè)參數(shù)默認(rèn)都設(shè)成0

接下來我們加入優(yōu)先級的參數(shù)夜牡,再來看一下結(jié)果

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
    NSLog(@"start task 1");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 1");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    NSLog(@"start task 2");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 2");
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    NSLog(@"start task 3");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 3");
});
帶有優(yōu)先級參數(shù)

結(jié)果很明顯已經(jīng)完全按照我們的優(yōu)先級進(jìn)行執(zhí)行的

DISPATCH_QUEUE_PRIORITY_HIGH>DISPATCH_QUEUE_PRIORITY_DEFAULT>DISPATCH_QUEUE_PRIORITY_LOW

相信大家已經(jīng)明白第一個(gè)參數(shù)的作用了吧与纽,如果你的需求需要某個(gè)并發(fā)線程先執(zhí)行,可以通過設(shè)置優(yōu)先級來達(dá)到目的塘装,但是因?yàn)榫€程是并發(fā)執(zhí)行的急迂,所以你不能保證哪個(gè)線程會先執(zhí)行完,也就是不能保證我們的耗時(shí)任務(wù)是按照順序執(zhí)行的蹦肴。
那么如何才能保證按順序執(zhí)行呢僚碎?這就需要我們自定義串行的queue來解決,系統(tǒng)為我們提供了這個(gè)方法dispatch_queue_create(const char *label, dispatch_queue_attr_t attr)阴幌,這個(gè)方法同樣也有兩個(gè)參數(shù)勺阐,第一個(gè)參數(shù)是確定唯一queue的一個(gè)標(biāo)識卷中,第二個(gè)參數(shù)創(chuàng)建queue的類型,串行的還是并行的渊抽。下面看一個(gè)例子:

/**
 *  我們創(chuàng)建了一個(gè)串行的queue
 *
 *  @param "com.gcd.test.queue"  唯一標(biāo)識這個(gè)queue
 *  @param DISPATCH_QUEUE_SERIAL 說明是串行的queue
 */
dispatch_queue_t myQueue = dispatch_queue_create("com.gcd.test.queue", DISPATCH_QUEUE_SERIAL);

dispatch_async(myQueue, ^{
    NSLog(@"start task 1");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 1");
});

dispatch_async(myQueue, ^{
    NSLog(@"start task 2");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 2");
});

dispatch_async(myQueue, ^{
    NSLog(@"start task 3");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 3");
});
串行隊(duì)列執(zhí)行結(jié)果

有兩點(diǎn)需要注意的地方蟆豫,第一,串行queue嚴(yán)格按照順序執(zhí)行耗時(shí)任務(wù)懒闷。第二十减,GCD在執(zhí)行串行queue時(shí),其實(shí)是在一個(gè)線程中完成的愤估,這樣就會嚴(yán)格的按照順序進(jìn)行執(zhí)行了帮辟,如上圖中紅色部分,42565是主線程ID灵疮, 1589679就是我們的異步線程ID织阅。而在并發(fā)的queue中,我們會發(fā)現(xiàn)線程ID都是不一樣的震捣,說明是多個(gè)線程。
如果我們在queue的創(chuàng)建時(shí)dispatch_queue_t myQueue = dispatch_queue_create("com.gcd.test.queue", DISPATCH_QUEUE_SERIAL)闹炉,我們把參數(shù)DISPATCH_QUEUE_SERIAL變成DISPATCH_QUEUE_CONCURRENT蒿赢,將會創(chuàng)建一個(gè)并發(fā)的queue,此處的結(jié)果和global_queue無異渣触,這里不在贅述羡棵。

dispatch_group_queue

GCD另一個(gè)比較常用的方法就是dispatch_group_queue,
在我們平時(shí)實(shí)際項(xiàng)目中經(jīng)常會有這樣的需求嗅钻,就是在多個(gè)任務(wù)異步處理后我們需要一個(gè)統(tǒng)一的回調(diào)通知去處理接下來的業(yè)務(wù)皂冰,這個(gè)時(shí)候我們就想到了dispatch group了,當(dāng)所有任務(wù)完成后會調(diào)用dispatch_group_notify养篓,來看一個(gè)例子:

/**
 *  創(chuàng)建一個(gè)并發(fā)的queue
 */
dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue", DISPATCH_QUEUE_CONCURRENT);
/**
 *  創(chuàng)建一個(gè)group
 */
dispatch_group_t group = dispatch_group_create();
/**
 *  執(zhí)行3個(gè)耗時(shí)任務(wù)
 */
dispatch_group_async(group, queue, ^{
    NSLog(@"start task 1");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 1");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"start task 2");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 2");
});

dispatch_group_async(group, queue, ^{
    NSLog(@"start task 3");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 3");
});
dispatch_group_notify(group, queue, ^{
    NSLog(@"task over");
});
執(zhí)行結(jié)果

當(dāng)三個(gè)異步耗時(shí)操作完成后秃流,統(tǒng)一一個(gè)回調(diào),兩點(diǎn)注意:

  1. 回調(diào)回來的數(shù)據(jù)異步線程中柳弄,這一點(diǎn)通過它前邊的線程ID號1631915就能發(fā)現(xiàn)舶胀。所以如果后續(xù)執(zhí)行刷新UI操作需要到主線程中完成。
  2. 接收回調(diào)的這個(gè)線程就是任務(wù)最后執(zhí)行完的那個(gè)線程碧注,系統(tǒng)做了優(yōu)化并沒有多開一個(gè)線程來處理嚣伐。

結(jié)合實(shí)際場景,很多同學(xué)比較容易犯一個(gè)錯(cuò)誤萍丐,假如我們有兩個(gè)請求需要同時(shí)發(fā)送轩端,并統(tǒng)一回調(diào),很多同學(xué)這時(shí)候就會想到了dipatch_group逝变。

dispatch_queue_t queue = dispatch_queue_create("com.test.gcd.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_t group = dispatch_group_create();

dispatch_group_async(group, queue, ^{
    [self sendRequest1:^{
    }];
});

dispatch_group_async(group, queue, ^{
    [self sendRequest2:^{
    }];
});

dispatch_group_notify(group, queue, ^{
    NSLog(@"task over");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"refresh ui");
    });
});

- (void)sendRequest1:(void(^)())block {
//異步請求基茵,請求結(jié)果后block回調(diào)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"start task 1");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 1");
    dispatch_async(dispatch_get_main_queue(), ^{
        if (block) {
            block();
        }
    });
});
}
- (void)sendRequest2:(void(^)())block {
//異步請求奋构,請求結(jié)果后block回調(diào)
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    NSLog(@"start task 2");
    [NSThread sleepForTimeInterval:3];
    NSLog(@"end task 2");
    dispatch_async(dispatch_get_main_queue(), ^{
        if (block) {
            block();
        }
    });
});
}

其中sendRequest1和sendRequest2是兩個(gè)異步請求,可以簡單理解為我們的業(yè)務(wù)請求API耿导,我們最初的設(shè)想是task1和task2完成后統(tǒng)一回調(diào)回來声怔,但結(jié)果確實(shí)這樣:

運(yùn)行結(jié)果

這是為什么呢?很明顯應(yīng)為sendRequest1和sendRequest2都是異步請求API舱呻,所以dispatch_group_async中瞬間就執(zhí)行完了醋火。腫么辦?難道針對這種需求就沒有辦法了嗎箱吕?當(dāng)然有芥驳,dispatch_group還有一個(gè)辦法能很好的解決這個(gè)問題,就是dispatch_group_enter() & dispatch_group_leave這對組合茬高。我們把這個(gè)需求重新實(shí)現(xiàn)一下:

dispatch_group_t group = dispatch_group_create();

dispatch_group_enter(group);
[self sendRequest1:^{
    dispatch_group_leave(group);
}];

dispatch_group_enter(group);
[self sendRequest2:^{
    dispatch_group_leave(group);
}];

dispatch_group_notify(group, queue, ^{
    NSLog(@"task over");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"refresh ui");
    });
});
運(yùn)行結(jié)果

這下就對了兆旬,完全按照我們的想法去執(zhí)行了。
dispatch_group_enter() 和 dispatch_group_leave()必須成對出現(xiàn)怎栽,group_enter是將請求任務(wù)放入到group后丽猬,便一直被group持有,直到碰到group_leave熏瞄;才會釋放出來脚祟,只有g(shù)roup中不在持有任何任務(wù)后才會調(diào)用notify進(jìn)行回調(diào)通知。

文章寫到這里基本已經(jīng)接近尾聲了强饮,當(dāng)然GCD還有很多其他用法由桌,常用的比如dispatch_once、dispatch_after邮丰、dispatch_barrier等等行您,本篇文章就不在做詳細(xì)說明。希望以上的內(nèi)容能對正在閱讀的你有所幫助剪廉。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末娃循,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子妈经,更是在濱河造成了極大的恐慌淮野,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件吹泡,死亡現(xiàn)場離奇詭異骤星,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)爆哑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進(jìn)店門洞难,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揭朝,你說我怎么就攤上這事队贱∩剑” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵柱嫌,是天一觀的道長锋恬。 經(jīng)常有香客問我,道長编丘,這世上最難降的妖魔是什么与学? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮嘉抓,結(jié)果婚禮上索守,老公的妹妹穿的比我還像新娘。我一直安慰自己抑片,他們只是感情好卵佛,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著敞斋,像睡著了一般截汪。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上植捎,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天挫鸽,我揣著相機(jī)與錄音,去河邊找鬼鸥跟。 笑死,一個(gè)胖子當(dāng)著我的面吹牛盔沫,可吹牛的內(nèi)容都是我干的医咨。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼架诞,長吁一口氣:“原來是場噩夢啊……” “哼拟淮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起谴忧,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤很泊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后沾谓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體委造,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年均驶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昏兆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妇穴,死狀恐怖爬虱,靈堂內(nèi)的尸體忽然破棺而出隶债,到底是詐尸還是另有隱情,我是刑警寧澤跑筝,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布死讹,位于F島的核電站,受9級特大地震影響曲梗,放射性物質(zhì)發(fā)生泄漏赞警。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一稀并、第九天 我趴在偏房一處隱蔽的房頂上張望仅颇。 院中可真熱鬧,春花似錦碘举、人聲如沸忘瓦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽耕皮。三九已至,卻和暖如春蝙场,著一層夾襖步出監(jiān)牢的瞬間凌停,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工售滤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留罚拟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓完箩,卻偏偏與公主長得像赐俗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子弊知,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容