- 實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求順序執(zhí)行的幾種方案及優(yōu)缺點(diǎn)比較
- 網(wǎng)絡(luò)請(qǐng)求順序執(zhí)行的具體實(shí)現(xiàn)
實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求順序執(zhí)行的方案
- 回調(diào)中發(fā)起下次請(qǐng)求
- 優(yōu)點(diǎn):最簡(jiǎn)單
- 缺點(diǎn):會(huì)產(chǎn)生回調(diào)地獄的問(wèn)題∫种樱回調(diào)套回調(diào)枫甲。
- dispatch_group
-
dispatch_group
的本質(zhì)實(shí)現(xiàn)還是通過(guò)的信號(hào)量機(jī)制颠毙,所以?xún)?yōu)缺點(diǎn)與信號(hào)量方式基本是一樣的魔招。只是API更加方便一些。
-
- 信號(hào)量
- 優(yōu)點(diǎn):系統(tǒng)API即可完成嵌溢,無(wú)需第三方支持眯牧,不會(huì)產(chǎn)生回調(diào)地獄問(wèn)題,是通過(guò)調(diào)度線(xiàn)程完成的赖草。
- 缺點(diǎn):當(dāng)請(qǐng)求的回調(diào)與wait在同一串行隊(duì)列的時(shí)候會(huì)發(fā)生死鎖学少。
- PromiseKit
- 優(yōu)點(diǎn):鏈?zhǔn)骄幊蹋a可讀性較高疚顷,本質(zhì)和回調(diào)方式是一樣的旱易。
- 缺點(diǎn):需要導(dǎo)入PromiseKit第三方庫(kù)
- 待補(bǔ)充...
代碼實(shí)現(xiàn)
請(qǐng)求的發(fā)起如下
- (void) requestOneWithSuccessBlock:(void(^)(void))successBlock {
AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"application/zip", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
NSLog(@"%@",R1_START);
[sessionManager GET:@"http://www.weather.com.cn/data/cityinfo/101190408.html" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@",R1_END);
if (successBlock) {
successBlock();
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}
- (void) requestTwoWithBlock:(void(^)(void))successBlock{
AFHTTPSessionManager *sessionManager = [AFHTTPSessionManager manager];
sessionManager.requestSerializer = [AFJSONRequestSerializer serializer];
sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"application/zip", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];
NSLog(@"%@", R2_START);
[sessionManager GET:@"http://wthrcdn.etouch.cn/weather_mini?city=%E5%8C%97%E4%BA%AC%E5%B8%82" parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"%@", R2_END);
if (successBlock) {
successBlock();
}
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
}];
}
信號(hào)量的方式實(shí)現(xiàn)
CODE
/*通過(guò)信號(hào)量的方式實(shí)現(xiàn)順序執(zhí)行*/
- (void)serialBySemaphore {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self requestOneWithSuccessBlock:^{
dispatch_semaphore_signal(semaphore);
}];
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
[self requestTwoWithBlock:^{
}];
});
}
CODE ANALYSIS
使用信號(hào)量需要注意,dispatch_semaphore_wait()
方法是會(huì)阻塞當(dāng)前線(xiàn)程腿堤,如果沒(méi)有接收到信號(hào)量阀坏,就一直阻塞當(dāng)前線(xiàn)程的執(zhí)行。
所以一定要注意網(wǎng)絡(luò)請(qǐng)求的回調(diào)是否和wait
在同一條串行隊(duì)列中笆檀。如果在同一條串行隊(duì)列則導(dǎo)致死鎖情況忌堂。
串行隊(duì)列的性質(zhì)導(dǎo)致了只會(huì)有一條線(xiàn)程來(lái)這個(gè)隊(duì)列取任務(wù)執(zhí)行,并且一個(gè)任務(wù)執(zhí)行完畢之后才會(huì)取下一個(gè)任務(wù)酗洒。當(dāng)請(qǐng)求發(fā)起之后線(xiàn)程就會(huì)執(zhí)行wait
操作士修,而當(dāng)請(qǐng)求回來(lái)之后,需要等待wait
之后才可以執(zhí)行樱衷。然而wait
又需要回調(diào)方法中的signal
操作才能繼續(xù)向下執(zhí)行棋嘲。相互等待導(dǎo)致死鎖發(fā)生。
這也就是為什么在方法的開(kāi)始將線(xiàn)程切換到子線(xiàn)程矩桂,AFNetworking
的回調(diào)方法如果沒(méi)有指定completionQueue
則默認(rèn)提交到在主隊(duì)列沸移,也就是在主線(xiàn)程執(zhí)行,而將信號(hào)量相關(guān)操作切換到子線(xiàn)程之后侄榴,阻塞的就是這條子線(xiàn)程雹锣,等到請(qǐng)求完成之后,主線(xiàn)程執(zhí)行回調(diào)方法癞蚕,釋放信號(hào)量蕊爵,這條子線(xiàn)程接收到信號(hào)量,繼續(xù)向下執(zhí)行桦山,發(fā)起下一個(gè)請(qǐng)求攒射。
信號(hào)量的三個(gè)方法介紹
dispatch_semaphore_create(0)
創(chuàng)建一個(gè)值為0信號(hào)量
dispatch_semaphore_signal(semaphore)
將信號(hào)量semaphore
的值增加1
dispatch_semaphore_wait(semaphore,time)
, 阻塞線(xiàn)程的執(zhí)行醋旦,等待信號(hào)量semaphore
,只有信號(hào)量的值大于0的時(shí)候才向下執(zhí)行匆篓。
dispatch_group
方式實(shí)現(xiàn)
CODE ONE
-(void) serialByGroupWait {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self requestOneWithSuccessBlock:^{
dispatch_group_leave(group);
}];
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
dispatch_group_enter(group);
[self requestTwoWithBlock:^{
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
NSLog(@"all request done!");
});
});
}
這種方式的實(shí)現(xiàn)方式的原理與信號(hào)量相似浑度,只是API不同。
CODE TWO
- (void) serialByGroupNotify {
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
[self requestOneWithSuccessBlock:^{
dispatch_group_leave(group);
}];
dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
[self requestTwoWithBlock:^{
}];
});
}
這種方式的不同就是講第二個(gè)請(qǐng)求放到了組內(nèi)任務(wù)完成的通知方法中鸦概。
當(dāng)group中的所有任務(wù)都完成了,會(huì)執(zhí)行notify
方法甩骏。本質(zhì)其實(shí)也是在監(jiān)聽(tīng)這個(gè)任務(wù)組中的信號(hào)量是否都已完成窗市。
回調(diào)中執(zhí)行的方式
CODE
- (void) serialByCallBack {
[self requestOneWithSuccessBlock:^{
[self requestTwoWithBlock:^{
}];
}];
}
DEMO
https://github.com/cocacola-ty/demos/tree/master/SerialNetRequest
作者:tianyu_f
鏈接:http://www.reibang.com/p/b1f963554489
來(lái)源:簡(jiǎn)書(shū)
簡(jiǎn)書(shū)著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請(qǐng)聯(lián)系作者獲得授權(quán)并注明出處饮笛。