在實際開發(fā)中遇見一個界面多個請求操作的問題

前言

最近項目在接入網(wǎng)絡(luò)接口時, 有個比較值得注意的地方, 就是一個界面存在多個網(wǎng)絡(luò)接口, 比如: 在首頁界面中存在多個網(wǎng)絡(luò)請求接口(包括: 圖片輪播器, 黃金產(chǎn)品以及黃金樣品保單). 如果按照平常那種思維來編碼, 很可能會出現(xiàn)UI刷新不出來的問題. 造成界面空白現(xiàn)象. 我先說一下背景: 公司使用的網(wǎng)絡(luò)類是對AFNetworking框架進(jìn)行了再度封裝, 雖然大家都對它再熟悉不過了, 但是細(xì)節(jié)上的東西還是需要慢慢品味. 本章文章主要涉及到的是GCD中幾個比較常見的函數(shù).重在基礎(chǔ), 大神可以忽略. 如果文章中存在問題, 希望大神在底部留言, 指導(dǎo)一下小白.
思路: 首頁中后臺提供了三個網(wǎng)絡(luò)請求接口, 目的就是當(dāng)所有的請求操作都完成之后, 才去刷新界面, 顯示界面. 腦袋中一閃而過的是GCD中的隊列組, 將請求操作添加隊列組, 最后在dispatch_group_notify中刷新UI.
 eg:  (注意: 在dispatch_group_notify中打印的順序是隨機(jī)的)
 
 dispatch_group_t group = dispatch_group_create();
 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
 NSLog(@"比如說這里是在子線程上的第一個請求");
 });
 
 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
 NSLog(@"比如說這里是子線程上的第二個請求");
 });
 
 dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
 
 NSLog(@"比如說這里是子線程上的第三個請求");
 });
 
 
 // 執(zhí)行完畢之后的通知
 dispatch_group_notify(group, dispatch_get_main_queue(), ^{
 
 
 NSLog(@"需要在這里回到主線程刷新UI");
 });

  • 我們先看看打印結(jié)果, 看看結(jié)果中有沒有什么貓膩
 打印結(jié)果:
 2017-02-15 09:22:08.287 text[987:26314] 比如說這里是子線程上的第二個請求
 2017-02-15 09:22:08.287 text[987:26311] 比如說這里是在子線程上的第一個請求
 2017-02-15 09:22:08.287 text[987:26312] 比如說這里是子線程上的第三個請求
 2017-02-15 09:22:08.302 text[987:26273] 需要在這里回到主線程刷新UI

解釋: 單純這個例子中,很難看出什么端倪, 但是這時候需要想到在開發(fā)中使用場景是網(wǎng)絡(luò)請求. 可能出現(xiàn)的情況: 在實際開發(fā)中, 我使用的AFN框架來實現(xiàn)網(wǎng)絡(luò)請求, AFN中的網(wǎng)絡(luò)請求都是異步操作, 就是說請求的數(shù)據(jù)返回后, 才會去刷新相關(guān)的UI
如果請求操作有多個, 所以必須要所有的操作都完成之后, 才去刷新UI,這樣就可能會造成一個現(xiàn)象, 就是數(shù)據(jù)是返回了, 但是刷新后UI不顯示.最后導(dǎo)致界面空白無物.

  • 解決方法
解決方法: 根據(jù)上述的現(xiàn)象, 這里需要引進(jìn)另一個函數(shù)
  • dispatch_semaphore(即: GCD中的信號量), 通過GCD中的信號量實現(xiàn)線程同步
    dispatch_semaphore概念: 信號量是基于計數(shù)器的一種多線程同步機(jī)制, 主要是用于解決多個線程訪問共有的資源時.造成數(shù)據(jù)紊亂的問題.
    dispatch_semaphore基本原理: 比如說網(wǎng)絡(luò)請求成功或者失敗之后需要將dispatch_semaphore計數(shù)器 +1, 請求網(wǎng)絡(luò)操作完成之后需要將dispatch_semaphore計數(shù)器 -1. 如果dispatch_semaphore計數(shù)器等于0表示等待.
    加1操作: dispatch_semaphore_signal(semaphore)
    等待操作: dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER)

  • 舉個例子: 每當(dāng)有顧客點餐,計數(shù)+1硝桩,點餐結(jié)束-1歸零繼續(xù)等待下一位顧客。比較類似于NSLock(線程鎖)。

 eg:
 - (void)request_A {
 
 //創(chuàng)建信號量并設(shè)置計數(shù)默認(rèn)為0
 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 
 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];;
 manager.responseSerializer = [AFHTTPResponseSerializer serializer];
 NSDictionary *parameter = @{@"key":@"value"
 };
 
 [manager POST:URL parameters:parameter progress:^(NSProgress * _Nonnull uploadProgress) {
 
 } success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
 
 //計數(shù)+1操作
 dispatch_semaphore_signal(sema);
    NSLog(@"在這里獲取數(shù)據(jù)");
 } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
 ////計數(shù)+1操作
 dispatch_semaphore_signal(sema);
     NSLog(@"在這里獲取error");
 }];
 
 //若計數(shù)為0則一直等待
 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
 }

為了便于記住, 一下是簡寫

 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
 [網(wǎng)絡(luò)請求:{
 成功:dispatch_semaphore_signal(sema);
 失敗:dispatch_semaphore_signal(sema);
 }];
 dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
 
 // 強行解釋一波
 通過使用GCD中的信號量可以解決多個操作共用同一資源時, 造成主線程阻塞的問題.
知識擴(kuò)展
  • 擴(kuò)展1: GCD中提供了函數(shù), 可以指定操作的執(zhí)行順序
> 擴(kuò)展: 如果我們要指定網(wǎng)絡(luò)操作的執(zhí)行順序的話, 直接使用GCD中的隊列, 然后添加依賴即可.
 eg:
 
 //1.任務(wù)一:
 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@" //1.任務(wù)一:")
 }];
 
 //2.任務(wù)二:
 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@" //1.任務(wù)二:")
 }];
 
 //3.任務(wù)三:
 NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@" //1.任務(wù)三:")
 }];
 
 //4.設(shè)置依賴
 [operation2 addDependency:operation1];      //任務(wù)二依賴任務(wù)一
 [operation3 addDependency:operation2];      //任務(wù)三依賴任務(wù)二
 
 //5.創(chuàng)建隊列并加入任務(wù)
 NSOperationQueue *queue = [[NSOperationQueue alloc] init];
 [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];

  • 擴(kuò)展2: 在Objctive-C中GCD提供了兩種方式來支持dispatch隊列的同步,就是dispatch組(隊列組)和信號量(dispatch_semaphore)

 
 // 創(chuàng)建隊列組
 dispatch_group_t group = dispatch_group_create();
 
 // 啟動隊列組中的block, 然后關(guān)聯(lián)到隊列組group中,
 

 隊列組的block操作
 
 @param group 隊列組
 @param queue#> 可以是全局的dispatch_get_global_queue(0, 0), 也可以是dispatch_get_main_queue()
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
    
    NSLog(@"具體的網(wǎng)絡(luò)請求操作");
});


 // 超時參數(shù)
 #define DISPATCH_TIME_NOW (0ull)   // 現(xiàn)在
 #define DISPATCH_TIME_FOREVER (~0ull)  // 一直
 
 // 表示: 等到group關(guān)聯(lián)的block執(zhí)行完畢

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

// 當(dāng)group組執(zhí)行完畢之后, 需要通知執(zhí)行完畢, 那么就會使用到GCD中的dispatch_barrier_async函數(shù)
// 主要隊列組中的操作執(zhí)行完畢后就會調(diào)用這個函數(shù)中block
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    
    NSLog(@"隊列組中的操作執(zhí)行完畢之后就會調(diào)用該函數(shù)");
});

//  我們也可以管理隊列組的運行狀態(tài)或者計數(shù), 使用下面兩個函數(shù)的時候需要注意的一點就是, 進(jìn)入或者退出, 他們的次數(shù)必須要匹配.
dispatch_group_enter(group); // 進(jìn)入
dispatch_group_leave(group);    // 退出
// 所以卡睦,我們也可以利用dispatch_group_enter币旧、 dispatch_group_leave和dispatch_group_wait來實現(xiàn)同步


  • 信號量


 二、dispatch信號量(dispatch semaphore)
 
 1. 創(chuàng)建信號量稀颁,可以設(shè)置信號量的資源數(shù)。0表示沒有資源楣黍,調(diào)用dispatch_semaphore_wait會立即等待匾灶。
 
 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
 
 2. 等待信號,可以設(shè)置超時參數(shù)租漂。該函數(shù)返回0表示得到通知阶女,非0表示超時。
 
 dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
 
 3. 通知信號哩治,如果等待線程被喚醒則返回非0秃踩,否則返回0。
 
 dispatch_semaphore_signal(semaphore);
 
 最后业筏,還是回到生成消費者的例子憔杨,使用dispatch信號量是如何實現(xiàn)同步:

總結(jié)

目前開發(fā)App越來越多樣化, 各種需求更是層出不窮, 所以很難想象在開發(fā)過程中你會遇到什么樣的需求, 不過, 再變態(tài)的需求, 也會有解決的方法, 本章的知識點雖然在開發(fā)中比較常見, 但是也值得注意, 然而記錄是學(xué)習(xí)最好的方法, 本章重在學(xué)習(xí)記錄, 如果有什么問題需要請朋友們不吝賜教, 后續(xù)會有更多精彩的開發(fā)總結(jié)..........
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市蒜胖,隨后出現(xiàn)的幾起案子消别,更是在濱河造成了極大的恐慌抛蚤,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件寻狂,死亡現(xiàn)場離奇詭異岁经,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)蛇券,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門缀壤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人怀读,你說我怎么就攤上這事诉位。” “怎么了菜枷?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵苍糠,是天一觀的道長。 經(jīng)常有香客問我啤誊,道長岳瞭,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任蚊锹,我火速辦了婚禮瞳筏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘牡昆。我一直安慰自己姚炕,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布丢烘。 她就那樣靜靜地躺著柱宦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪播瞳。 梳的紋絲不亂的頭發(fā)上掸刊,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機(jī)與錄音赢乓,去河邊找鬼忧侧。 笑死,一個胖子當(dāng)著我的面吹牛牌芋,可吹牛的內(nèi)容都是我干的蚓炬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼躺屁,長吁一口氣:“原來是場噩夢啊……” “哼试吁!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起楼咳,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤熄捍,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后母怜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體余耽,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年苹熏,在試婚紗的時候發(fā)現(xiàn)自己被綠了碟贾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡轨域,死狀恐怖袱耽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情干发,我是刑警寧澤朱巨,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站枉长,受9級特大地震影響冀续,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜必峰,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一洪唐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吼蚁,春花似錦凭需、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至术唬,卻和暖如春薪伏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背粗仓。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工嫁怀, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人借浊。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓塘淑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蚂斤。 傳聞我的和親對象是個殘疾皇子存捺,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

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