iOS處理同時(shí)回調(diào)多個(gè)請(qǐng)求結(jié)果——基于AFNetworking和GCD

實(shí)際工作中我們經(jīng)常會(huì)遇到有接口需要同時(shí)返回請(qǐng)求結(jié)果的情況奸晴,比如某一個(gè)詳情頁绽诚,可能有詳情信息和評(píng)論信息等多個(gè)接口需要請(qǐng)求魄藕,并且當(dāng)多個(gè)接口全部完成的時(shí)候内列,刷新當(dāng)前頁面的數(shù)據(jù),這里由于請(qǐng)求是異步的關(guān)系背率,我們不知道具體哪個(gè)請(qǐng)求會(huì)需要多久時(shí)間才能完成话瞧,所以今天分析一下解決方案嫩与。
在之前的《GCD的使用和原理》一文中有簡單講述這個(gè)問題,本文會(huì)針對(duì)這個(gè)問題詳細(xì)討論交排。
首先一個(gè)案例划滋,如果存在多個(gè)異步處理,如何能知道同時(shí)完成呢埃篓,這里能想到的是使用dispatch_group_async的方式將異步處理放入一個(gè)組中处坪,再使用dispatch_group_notify獲得所有組中異步完成的通知回調(diào)。

    NSLog(@"全部開始-----%@", [NSThread currentThread]);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_async(group, queue, ^{
        sleep(4);
        NSLog(@"子線程1-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_async(group, queue, ^{
        sleep(3);
        NSLog(@"子線程2-----%@", [NSThread currentThread]);
    });
    
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);
    });

這里簡單模擬了異步的耗時(shí)操作都许,我們需要的結(jié)果是兩個(gè)子線程的任務(wù)全部完成之后稻薇,才回到主線程繼續(xù)任務(wù)嫂冻,下面看一下打印結(jié)果

全部開始-----<NSThread: 0x6040000741c0>{number = 1, name = main}
子線程2-----<NSThread: 0x604000277380>{number = 5, name = (null)}
子線程1-----<NSThread: 0x604000469080>{number = 4, name = (null)}
全部結(jié)束-----<NSThread: 0x6040000741c0>{number = 1, name = main}

這里可以很明顯的看出開始和結(jié)束都是主線程胶征,而結(jié)束之前的確執(zhí)行了兩個(gè)子線程的耗時(shí)任務(wù)

這里實(shí)現(xiàn)了我們的需求,當(dāng)然不只是一種方式可以實(shí)現(xiàn)桨仿,下面再嘗試一種方法看看睛低。

    NSLog(@"全部開始-----%@", [NSThread currentThread]);
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_async(queue, ^{
        sleep(4);
        NSLog(@"子線程1-----%@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_async(queue, ^{
        sleep(3);
        NSLog(@"子線程2-----%@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"全部結(jié)束-----%@", [NSThread currentThread]);

這次沒有使用dispatch_group的方式,這個(gè)semaphore應(yīng)該也比較常用了(如果不常用的可以看我之前的關(guān)于GCD那篇文章)服傍,我們使用信號(hào)量的方式钱雷,使用wait阻止主線程后續(xù)任務(wù)的開展,當(dāng)信號(hào)量不為0的時(shí)候會(huì)-1并繼續(xù)執(zhí)行吹零,如果信號(hào)量為0罩抗,則等待,在得到信號(hào)增加之前就會(huì)持續(xù)等待灿椅,后面參數(shù)為時(shí)間類型表示永久套蒂,當(dāng)每個(gè)異步任務(wù)完成后,會(huì)給semaphore信號(hào)量+1茫蛹,當(dāng)所有任務(wù)完成之后操刀,主線程不再被阻止,會(huì)繼續(xù)任務(wù)婴洼。打印結(jié)果同上骨坑,這里不重復(fù)寫了,好奇的小伙伴們可以試下柬采。

以上使用了兩種方式解決異步任務(wù)的全部完成通知欢唾,下面該解決本文的需求了,就是當(dāng)多個(gè)請(qǐng)求全部完成的通知該怎么獲得粉捻。
有些朋友好奇多個(gè)請(qǐng)求和多個(gè)異步任務(wù)有什么區(qū)別匈辱,這里解釋下,多個(gè)異步任務(wù)我們可以指定這幾個(gè)異步任務(wù)為同一個(gè)組的任務(wù)杀迹,以及通過組的notify方法得到任務(wù)結(jié)束的通知亡脸,但是多個(gè)請(qǐng)求沒有這種條件押搪,所以不能完全用之前的方式處理。
先給一下錯(cuò)誤示范案例:

    NSLog(@"即將開始多個(gè)請(qǐng)求");
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^{
        [[URLBase sharedInstance] getBusinessDataWithSuccess:^(NSDictionary *result) {
            NSLog(@"請(qǐng)求1完成");
        } failure:^(NSError *error) {
        }];
    });
    dispatch_group_async(group, queue, ^{
        [[URLBase sharedInstance] getHomeConfigurationWithSuccess:^(NSDictionary *result) {
            NSLog(@"請(qǐng)求2完成");
        } failure:^(NSError *error) {
        }];
    });
    dispatch_group_notify(group, queue, ^{
        NSLog(@"全部完成");
    });

這里用了兩個(gè)請(qǐng)求任務(wù)放入異步操作中浅碾,下面看打印結(jié)果是否和我們預(yù)想的一樣

2018-09-11 22:43:04.469787+0800 Demo[2511:1215843] 即將開始多個(gè)請(qǐng)求
2018-09-11 22:43:04.472144+0800 Demo[2511:1216152] 全部完成
2018-09-11 22:43:05.598410+0800 Demo[2511:1215843] 請(qǐng)求1完成
2018-09-11 22:43:05.604425+0800 Demo[2511:1215843] 請(qǐng)求2完成

這里的時(shí)間很明顯告訴我們大州,同時(shí)返回的通知并沒有完成,原因是當(dāng)我們使用請(qǐng)求的時(shí)候垂谢,相當(dāng)于又一次開啟了一個(gè)異步操作厦画,請(qǐng)求的代碼會(huì)立即執(zhí)行完成,但是請(qǐng)求完成的異步回調(diào)并不能滥朱,所以我們要稍微改良一下我們的代碼

NSLog(@"即將開始多個(gè)請(qǐng)求");
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        [[URLBase sharedInstance] getBusinessDataWithSuccess:^(NSDictionary *result) {
            dispatch_group_leave(group);
            NSLog(@"請(qǐng)求1完成");
        } failure:^(NSError *error) {
        }];
    });
    
    dispatch_group_async(group, queue, ^{
        dispatch_group_enter(group);
        [[URLBase sharedInstance] getHomeConfigurationWithSuccess:^(NSDictionary *result) {
            dispatch_group_leave(group);
            NSLog(@"請(qǐng)求2完成");
        } failure:^(NSError *error) {
        }];
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"全部完成");
    });

我們的改動(dòng)很小根暑,只有在請(qǐng)求之前加了enter函數(shù),以及請(qǐng)求成功之后加入leave函數(shù)徙邻,下面看一下成功的結(jié)果

2018-09-11 22:48:41.626897+0800 Gemii[2518:1218253] 即將開始多個(gè)請(qǐng)求
2018-09-11 22:48:45.881502+0800 Gemii[2518:1218253] 請(qǐng)求1完成
2018-09-11 22:48:48.982129+0800 Gemii[2518:1218253] 請(qǐng)求2完成
2018-09-11 22:48:48.984066+0800 Gemii[2518:1218425] 全部完成

很明顯我們的需求達(dá)到了排嫌,解決方案就是當(dāng)請(qǐng)求之前調(diào)用dispatch_group_enter()函數(shù),表明即將進(jìn)入另一個(gè)內(nèi)部操作中缰犁,后續(xù)任務(wù)暫時(shí)停止淳地,直到請(qǐng)求成功之后,調(diào)用dispatch_group_leave()函數(shù)帅容,表明另一個(gè)內(nèi)部操作完成颇象,可以進(jìn)行接下來的操作,于是兩個(gè)group_async同時(shí)完成并徘,調(diào)用notify通知

上面很清楚我們用dispatch_group_enter和dispatch_group_leave這一對(duì)函數(shù)實(shí)現(xiàn)了我們的需求遣钳,下面仍然用semaphore的方式處理這個(gè)問題,不過semaphore如果像之前一樣操作麦乞,同樣會(huì)出問題蕴茴,案例如下

dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    NSLog(@"即將開始多個(gè)請(qǐng)求");

    [[URLBase sharedInstance] getBusinessDataWithSuccess:^(NSDictionary *result) {
        NSLog(@"請(qǐng)求1完成");
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSError *error) {
    }];
    [[URLBase sharedInstance] getHomeConfigurationWithSuccess:^(NSDictionary *result) {
        NSLog(@"請(qǐng)求2完成");
        dispatch_semaphore_signal(semaphore);
    } failure:^(NSError *error) {
    }];
    
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"全部完成");

這里的代碼也很清晰,和前文一樣路幸,我們認(rèn)為當(dāng)請(qǐng)求成功之后荐开,將信號(hào)量+1,直到兩個(gè)請(qǐng)求都完成解決wait的阻塞简肴,但是我們看一下結(jié)果

2018-09-11 23:12:55.898884+0800 Gemii[2600:1231519] 即將開始多個(gè)請(qǐng)求

這里只打印了開始的部分晃听,后續(xù)任務(wù)全部沒有執(zhí)行,并且頁面進(jìn)入了死鎖狀態(tài)砰识,造成這個(gè)的原因是什么呢能扒,看似和之前一樣,應(yīng)該沒有問題辫狼,但是這里注意初斑,我們主線程一開始就已經(jīng)進(jìn)入了wait狀態(tài),之前的方式是在異步的子線程中膨处,增加信號(hào)量见秤,但是我們這里想回到主線程的block已經(jīng)被wait擋住不能執(zhí)行砂竖,導(dǎo)致不能調(diào)用dispatch_semaphore_signal()函數(shù)增加信號(hào)量,所以造成了死鎖鹃答,那么解決辦法就是將請(qǐng)求放到異步隊(duì)列中

NSLog(@"即將開始多個(gè)請(qǐng)求");
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
    dispatch_group_async(group, queue, ^{
        [[URLBase sharedInstance] getBusinessDataWithSuccess:^(NSDictionary *result) {
            NSLog(@"請(qǐng)求1完成---%@", [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        } failure:^(NSError *error) {
        }];
    });
    dispatch_group_async(group, queue, ^{
        [[URLBase sharedInstance] getHomeConfigurationWithSuccess:^(NSDictionary *result) {
            NSLog(@"請(qǐng)求2完成---%@", [NSThread currentThread]);
            dispatch_semaphore_signal(semaphore);
        } failure:^(NSError *error) {
        }];
    });
    
    dispatch_group_notify(group, queue, ^{
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
        NSLog(@"全部完成---%@", [NSThread currentThread]);
    });

這里我們雖然使用了group的方式乎澄,但是沒有使用enter和leave兩個(gè)函數(shù),而是為了避免semaphore會(huì)在主線程死鎖而加入了異步操作测摔,下面是成功結(jié)果

2018-09-11 23:25:30.054214+0800 即將開始多個(gè)請(qǐng)求
2018-09-11 23:25:30.705859+0800 請(qǐng)求1完成---<NSThread: 0x1c02623c0>{number = 1, name = main}
2018-09-11 23:25:30.905989+0800 請(qǐng)求2完成---<NSThread: 0x1c02623c0>{number = 1, name = main}
2018-09-11 23:25:30.906441+0800 全部完成---<NSThread: 0x1c0870d40>{number = 11, name = (null)}

這樣看就很清晰置济,兩個(gè)請(qǐng)求完成是在主線程完成的回調(diào),但是wait以及完成后操作锋八,均在子線程中浙于,不會(huì)對(duì)主線程造成阻塞,所以可以實(shí)現(xiàn)本文需求

到這里我使用了兩種方式實(shí)現(xiàn)標(biāo)題的需求挟纱,有更多的解決辦法還期待大家的探索羞酗,這里就不多加贅述了,如果有更方便的方式樊销,歡迎在下方評(píng)論區(qū)進(jìn)行討論整慎,如果文章中有技術(shù)錯(cuò)誤還請(qǐng)指正脏款。
按照之前說的围苫,后續(xù)文章會(huì)討論多個(gè)請(qǐng)求同步執(zhí)行的方法,最近時(shí)間比較緊撤师,更新比較慢還請(qǐng)見諒剂府,如果有認(rèn)為本文有些作用的請(qǐng)點(diǎn)個(gè)贊支持下,感謝各方大佬~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末剃盾,一起剝皮案震驚了整個(gè)濱河市腺占,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌痒谴,老刑警劉巖衰伯,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異积蔚,居然都是意外死亡意鲸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門尽爆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來怎顾,“玉大人,你說我怎么就攤上這事漱贱』蔽恚” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵幅狮,是天一觀的道長募强。 經(jīng)常有香客問我株灸,道長,這世上最難降的妖魔是什么擎值? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任蚂且,我火速辦了婚禮,結(jié)果婚禮上幅恋,老公的妹妹穿的比我還像新娘阶淘。我一直安慰自己惜傲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著要尔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪筐摘。 梳的紋絲不亂的頭發(fā)上鸦泳,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音肉瓦,去河邊找鬼遭京。 笑死,一個(gè)胖子當(dāng)著我的面吹牛泞莉,可吹牛的內(nèi)容都是我干的哪雕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼鲫趁,長吁一口氣:“原來是場噩夢啊……” “哼斯嚎!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起挨厚,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤堡僻,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后疫剃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體钉疫,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年巢价,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了牲阁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蹄溉,死狀恐怖咨油,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情柒爵,我是刑警寧澤役电,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站棉胀,受9級(jí)特大地震影響法瑟,放射性物質(zhì)發(fā)生泄漏冀膝。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一霎挟、第九天 我趴在偏房一處隱蔽的房頂上張望窝剖。 院中可真熱鬧,春花似錦酥夭、人聲如沸赐纱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽疙描。三九已至,卻和暖如春讶隐,著一層夾襖步出監(jiān)牢的瞬間起胰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工巫延, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留效五,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓炉峰,卻偏偏與公主長得像畏妖,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子讲冠,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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