使用dispatch_group實現並封裝分組並髮網絡請求

來源:互聯網
上載者:User

標籤:

在實際開發中我們通常會遇到這樣一種需求:某個頁面載入時通過網路請求獲得相應的資料,再做某些操作。有時候載入的內容需要通過好幾個請求的資料群組合而成,比如有兩個請求A和B,我們通常為了省事,會將B請求放在A請求成功的回調中發起,在B的成功回調中將資料群組合起來,這樣做有明顯的問題:

1.請求如果多了,需要寫許多嵌套的請求

2.如果在除了最後一個請求前的某個請求失敗了,就不會執行後面的請求,資料無法載入

3.請求變成同步的,這是最大的問題,在網路差的情況下,如果有n個請求,意味著使用者要等待n倍於並發請求的時間才能看到內容

 

同步請求這麼low的方式當然是不可接受的,所以我們要並發這些請求,在所有請求都執行完成功回調後,再做載入內容或其他動作,考慮再三,選擇用GCD的dispatch_group。

dispatch_group通常有兩種用法,一種是

dispatch_group_async(<#dispatch_group_t group#>, <#dispatch_queue_t queue#>, <#^(void)block#>)

建立一個dispatch_group_t, 將並發的操作放在block中,在

dispatch_group_notify(<#dispatch_group_t group#>, <#dispatch_queue_t queue#>, <#^(void)block#>)

的block中執行多組block執行完畢後的操作,對於網路請求來說,在請求發出時他就算執行完畢了,並不會等待回調,所以不滿足我們的需求。

所以採用另一種用法:

使用dispatch_group_enter和dispatch_group_leave,這種方式使用更為靈活,enter和leave必須配合使用,有幾次enter就要有幾次leave,否則group會一直存在。當所有enter的block都leave後,會執行dispatch_group_notify的block。

我們當然可以在網路請求前enter,在執行完每個請求的成功回調後leave,再在notify中執行內容載入,這樣看來問題就解決了,但還是有點小小不爽,每次發起組請求我都得建立group,寫一堆的enter和leave,既麻煩也不利於複用,很自然我們想到把他封裝一下,最好能做到將一個網路請求加到組裡,而不用修改原先的網路請求代碼,就像這樣:

[[MDXuexiBaoAPI sharedInstance] postForGroup:^{        request1 success:^(id responseObject) {                    } failure:^(NSError *error) {                    }];                request2 success:^(id responseObject) {        } failure:^(NSError *error) {                   }];    } success:^{        // group success    } failure:^(NSArray *errorArray) {        // group failure    }]

如果我想做到這種效果,肯定要到網路單例層去做些修改,但我又不想改變現有的底層要求方法,所以我採用了

method_exchangeImplementations(<#Method m1#>, <#Method m2#>) 這個函數,基於現有的底層要求方法,實現一套組的要求方法,替換掉原先的方法,在組請求都發送完畢後,再換回原先的方法。

但這裡有一些可怕的坑要處理,因為使用方法替換是很危險的。

1.我做了替換後,正常的非組網路請求也會走替換後的方法,但我不需要他走替換後的方法。

2.假如我同時發起了多個組請求,組和組之間要如何區分,不同的組是不應該相互影響的。

 

一開始我考慮給請求一個mark,標記他是屬於哪個group的,但這需要你已經把請求封裝成了一個對象,如果你的項目和我的一樣,只是執行一個方法,是不好給他加標記的。

在一陣頭腦風暴後,我決定用隊列來區分每個gorup。

具體做法就是建立每個group時,開啟一個隊列,給隊列動態添加group屬性,一個隊列對應一個group。在隊列中替換方法,發起組裡的請求,再替換回原先的方法,這樣在replaced method裡只需要拿到當前的隊列,就可以拿到group,如果group是nil,說明是正常的非組請求,執行original method;如果group不是nil,根據group來enter和leave,每個group也能區分開。

建立group時,給group添加了一個errorArray,用來記錄組裡請求的error,只要errorArray不為空白,就會走組失敗的block。

下面上代碼:

 

typedef void(^BlockAction)();typedef void(^GroupResponseFailure)(NSArray * errorArray);static char groupErrorKey;static char queueGroupKey;
- (void)sendPOSTRequestInGroup:(NSString *)strURL withData:(NSDictionary *)data paramForm:(ParamForm)paramForm withTimeout:(NSTimeInterval)timeout showAlert:(BOOL)show  success:(BlockResponse)success failure:(BlockResponseFailure)failure {        dispatch_group_t group = objc_getAssociatedObject([NSOperationQueue currentQueue], &queueGroupKey);        // 如果是非組請求    if (group == nil) {        // 執行original method        [self sendPOSTRequestInGroup:strURL withData:data paramForm:paramForm withTimeout:timeout showAlert:show success:success failure:failure];        return;    }        dispatch_group_enter(group);    // 執行original method    [self sendPOSTRequestInGroup:strURL withData:data paramForm:paramForm withTimeout:timeout showAlert:show success:^(id responseObject) {        success(responseObject);        dispatch_group_leave(group);    } failure:^(NSError *error) {        NSMutableArray *arrayM = objc_getAssociatedObject(group, &groupErrorKey);        [arrayM addObject:error];        failure(error);        dispatch_group_leave(group);    }];}- (void)sendGroupPostRequest:(BlockAction)requests success:(BlockAction)success failure:(GroupResponseFailure)failure {    if (requests == nil) {        return;    }        dispatch_group_t group = dispatch_group_create();    objc_setAssociatedObject(group, &groupErrorKey, [NSMutableArray array], OBJC_ASSOCIATION_RETAIN_NONATOMIC);        Method originalPost = class_getInstanceMethod(self.class, @selector(sendPOSTRequest:withData:paramForm:withTimeout:showAlert:success:failure:));    Method groupPost = class_getInstanceMethod(self.class, @selector(sendPOSTRequestInGroup:withData:paramForm:withTimeout:showAlert:success:failure:));        NSOperationQueue *queue = [[NSOperationQueue alloc] init];    objc_setAssociatedObject(queue, &queueGroupKey, group, OBJC_ASSOCIATION_RETAIN_NONATOMIC);    queue.qualityOfService = NSQualityOfServiceUserInitiated;    queue.maxConcurrentOperationCount = 3;        [queue addOperationWithBlock:^{                method_exchangeImplementations(originalPost, groupPost);        requests();        // 發出請求後就可以替換回original method,不必等待回調,盡量減小替換的時間視窗        method_exchangeImplementations(originalPost, groupPost);                dispatch_group_notify(group, dispatch_get_main_queue(), ^{            NSMutableArray *arrayM = objc_getAssociatedObject(group, &groupErrorKey);            if (arrayM.count > 0) {                if (failure) {                    failure(arrayM.copy);                }            } else if(success) {                success();            }                    });    }];}

 

- (void)sendPOSTRequestInGroup:(NSString *)strURL withData:(NSDictionary *)data paramForm:(ParamForm)paramForm withTimeout:(NSTimeInterval)timeout showAlert:(BOOL)show success:(BlockResponse)success failure:(BlockResponseFailure)failure
替換網路工具層的底層post請求

sendPOSTRequest:withData:paramForm:withTimeout:showAlert:success:failure:

這樣在 

- (void)sendGroupPostRequest:(BlockAction)requests success:(BlockAction)success failure:(GroupResponseFailure)failure
requests block中,把網路請求扔進去,單個請求本身的success和failure都能執行,success執行組成功的代碼,failure中可以拿到每個請求的error,作相應處理。

使用dispatch_group實現並封裝分組並髮網絡請求

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.