- 實(shí)現(xiàn)網(wǎng)絡(luò)請求順序執(zhí)行的幾種方案及優(yōu)缺點(diǎn)比較
- 網(wǎng)絡(luò)請求順序執(zhí)行的具體實(shí)現(xiàn)
實(shí)現(xiàn)網(wǎng)絡(luò)請求順序執(zhí)行的方案
- 回調(diào)中發(fā)起下次請求
- 優(yōu)點(diǎn):最簡單
- 缺點(diǎn):會產(chǎn)生回調(diào)地獄的問題姜骡〖韫埽回調(diào)套回調(diào)。
- dispatch_group
-
dispatch_group
的本質(zhì)實(shí)現(xiàn)還是通過的信號量機(jī)制沿彭,所以優(yōu)缺點(diǎn)與信號量方式基本是一樣的寸莫。只是API更加方便一些。
-
- 信號量
- 優(yōu)點(diǎn):系統(tǒng)API即可完成鹿寨,無需第三方支持新博,不會產(chǎn)生回調(diào)地獄問題,是通過調(diào)度線程完成的脚草。
- 缺點(diǎn):當(dāng)請求的回調(diào)與wait在同一串行隊(duì)列的時(shí)候會發(fā)生死鎖赫悄。
- PromiseKit
- 優(yōu)點(diǎn):鏈?zhǔn)骄幊蹋a可讀性較高馏慨,本質(zhì)和回調(diào)方式是一樣的埂淮。
- 缺點(diǎn):需要導(dǎo)入PromiseKit第三方庫
- 待補(bǔ)充...
代碼實(shí)現(xiàn)
請求的發(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) {
}];
}
信號量的方式實(shí)現(xiàn)
CODE
/*通過信號量的方式實(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
使用信號量需要注意,dispatch_semaphore_wait()
方法是會阻塞當(dāng)前線程写隶,如果沒有接收到信號量同诫,就一直阻塞當(dāng)前線程的執(zhí)行。
所以一定要注意網(wǎng)絡(luò)請求的回調(diào)是否和wait
在同一條串行隊(duì)列中樟澜。如果在同一條串行隊(duì)列則導(dǎo)致死鎖情況。
串行隊(duì)列的性質(zhì)導(dǎo)致了只會有一條線程來這個(gè)隊(duì)列取任務(wù)執(zhí)行叮盘,并且一個(gè)任務(wù)執(zhí)行完畢之后才會取下一個(gè)任務(wù)秩贰。當(dāng)請求發(fā)起之后線程就會執(zhí)行wait
操作,而當(dāng)請求回來之后柔吼,需要等待wait
之后才可以執(zhí)行毒费。然而wait
又需要回調(diào)方法中的signal
操作才能繼續(xù)向下執(zhí)行。相互等待導(dǎo)致死鎖發(fā)生愈魏。
這也就是為什么在方法的開始將線程切換到子線程觅玻,AFNetworking
的回調(diào)方法如果沒有指定completionQueue
則默認(rèn)提交到在主隊(duì)列想际,也就是在主線程執(zhí)行,而將信號量相關(guān)操作切換到子線程之后溪厘,阻塞的就是這條子線程胡本,等到請求完成之后,主線程執(zhí)行回調(diào)方法畸悬,釋放信號量侧甫,這條子線程接收到信號量,繼續(xù)向下執(zhí)行蹋宦,發(fā)起下一個(gè)請求披粟。
信號量的三個(gè)方法介紹
dispatch_semaphore_create(0)
創(chuàng)建一個(gè)值為0信號量
dispatch_semaphore_signal(semaphore)
將信號量semaphore
的值增加1
dispatch_semaphore_wait(semaphore,time)
, 阻塞線程的執(zhí)行,等待信號量semaphore
冷冗,只有信號量的值大于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)方式的原理與信號量相似,只是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è)請求放到了組內(nèi)任務(wù)完成的通知方法中拇泛。
當(dāng)group中的所有任務(wù)都完成了,會執(zhí)行notify
方法须板。本質(zhì)其實(shí)也是在監(jiān)聽這個(gè)任務(wù)組中的信號量是否都已完成碰镜。
回調(diào)中執(zhí)行的方式
CODE
- (void) serialByCallBack {
[self requestOneWithSuccessBlock:^{
[self requestTwoWithBlock:^{
}];
}];
}
DEMO
https://github.com/cocacola-ty/demos/tree/master/SerialNetRequest