在APP開發(fā)中,我們經(jīng)常會(huì)遇到下面兩種需求:
1.同時(shí)做多個(gè)網(wǎng)絡(luò)請求,所有網(wǎng)絡(luò)請求都完成后進(jìn)行下一步的操作。我們暫時(shí)稱之為并發(fā)網(wǎng)路請求
2.依次做多個(gè)網(wǎng)絡(luò)請求,最后一個(gè)網(wǎng)絡(luò)請求完成后進(jìn)行下一步的操作。我們暫時(shí)稱之為依次網(wǎng)路請求
總結(jié)起來如下圖

網(wǎng)絡(luò)請求.jpeg
提供幾種不同的解決方法
- 并發(fā)網(wǎng)絡(luò)請求
解決方法
1.dispatch_group_t解決 - 依次網(wǎng)絡(luò)
解決方法
1.NSOperationQueue 線程依賴+dispatch_semaphore_wait解決
2.RAC-Concat解決
3.NSConditionLock解決
附件:簡單AFNetworking封裝
下面我們以實(shí)際例子作介紹。。。
請求路徑
static NSString *url = @"https://api.douban.com/v2/book/1220562";
一、并發(fā)網(wǎng)絡(luò)請求
1.使用dispatch_group_t解決
1.1:不需要對每一次請求結(jié)果進(jìn)行處理、只需知道什么視乎請求完成
- (void)request1 {
// 不需要對每一次請求結(jié)果進(jìn)行處理、只需知道什么視乎請求完成
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < 3; i++) {
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//請求3
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_group_leave(group);
NSLog(@"請求-Success");
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
NSLog(@"請求-Fail");
}];
});
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有請求完成,進(jìn)行下一步操作");
});
}
打印結(jié)果:
2018-07-22 10:38:51.010669+0800 LPExample[6614:330141] 請求3-Success
2018-07-22 10:38:51.030527+0800 LPExample[6614:330141] 請求1-Success
2018-07-22 10:38:51.038626+0800 LPExample[6614:330141] 請求2-Success
2018-07-22 10:38:51.038787+0800 LPExample[6614:330141] 所有請求完成,進(jìn)行下一步操作
1.2: 需要對每一次請求結(jié)果進(jìn)行處理、每個(gè)請求完成或者失敗后,處理參數(shù)
- (void)request2 {
// 需要對每一次請求結(jié)果進(jìn)行處理、每個(gè)請求完成或者失敗后,處理參數(shù)
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//請求1
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_group_leave(group);
NSLog(@"請求1-Success");
// 對請求1responseObject進(jìn)行處理
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
NSLog(@"請求1-Fail");
// 對請求1error進(jìn)行處理
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//請求2
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_group_leave(group);
NSLog(@"請求2-Success");
// 對請求2responseObject進(jìn)行處理
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
NSLog(@"請求2-Fail");
// 對請求2error進(jìn)行處理
}];
});
dispatch_group_enter(group);
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//請求3
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_group_leave(group);
NSLog(@"請求3-Success");
// 對請求3responseObject進(jìn)行處理
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
NSLog(@"請求3-Fail");
// 對請求3error進(jìn)行處理
}];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有請求完成,進(jìn)行下一步操作");
});
}
打印結(jié)果:
2018-07-22 11:59:11.099108+0800 LPExample[7640:386983] 請求2-Success
2018-07-22 11:59:11.112569+0800 LPExample[7640:386983] 請求1-Success
2018-07-22 11:59:11.146874+0800 LPExample[7640:386983] 請求3-Success
2018-07-22 11:59:11.147018+0800 LPExample[7640:386983] 所有請求完成,進(jìn)行下一步操作
使用dispatch_group_enter(group)和dispatch_group_leave(group)需注意:
enter和leave必須配合使用,有幾次enter就要有幾次leave,否則group會(huì)一直存在。當(dāng)所有enter的block都leave后,會(huì)執(zhí)行dispatch_group_notify的block。
也可以使用下面判斷
-(void)request {
NSArray <NSString *>*array = @[@"http://www.weather.com.cn/data/sk/101010100.html",@"https://api.douban.com/v2/book/1220562",@"https://api.apiopen.top/singlePoetry",];
dispatch_group_t group = dispatch_group_create();
[array enumerateObjectsUsingBlock:^(NSString * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
dispatch_group_enter(group);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//請求3
[[SPRequest request] GET:obj parameters:nil success:^(SPRequest *request, NSString *responseString) {
dispatch_group_leave(group);
if (idx == 0) {
//請求0的結(jié)果
} else if (idx == 1) {
//請求1的結(jié)果
} else if (idx == 2) {
//請求2的結(jié)果
}
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
if (idx == 0) {
//請求0的結(jié)果
} else if (idx == 1) {
//請求1的結(jié)果
} else if (idx == 2) {
//請求2的結(jié)果
}
}];
});
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"\n所有請求完成,進(jìn)行下一步操作");
});
}
二、 依次網(wǎng)絡(luò)解決方法
1.使用NSOperationQueue 線程依賴 + dispatch_semaphore_wait解決
- (void)request3 {
// 請求1
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_semaphore_signal(sema);
NSLog(@"請求1-Success");
} failure:^(SPRequest *request, NSError *error) {
dispatch_semaphore_signal(sema);
NSLog(@"請求1-Fail");
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}];
// 請求2
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_semaphore_signal(sema);
NSLog(@"請求2-Success");
} failure:^(SPRequest *request, NSError *error) {
dispatch_semaphore_signal(sema);
NSLog(@"請求2-Fail");
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}];
// 請求3
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_semaphore_signal(sema);
NSLog(@"請求3-Success");
} failure:^(SPRequest *request, NSError *error) {
dispatch_semaphore_signal(sema);
NSLog(@"請求3-Fail");
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}];
// 4.設(shè)置依賴
[operation2 addDependency:operation1]; //請求2依賴請求1
[operation3 addDependency:operation2]; //請求3依賴請求2
// 5.創(chuàng)建隊(duì)列并加入任務(wù)
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
}
打印結(jié)果:
2018-07-22 11:17:12.646628+0800 LPExample[7141:357438] 請求1-Success
2018-07-22 11:17:12.852751+0800 LPExample[7141:357438] 請求2-Success
2018-07-22 11:17:13.053805+0800 LPExample[7141:357438] 請求3-Success
注意:
絕對不要在應(yīng)用主線程中等待一個(gè)Operation,只能在第二或次要線程中等待。阻塞主線程將導(dǎo)致應(yīng)用無法響應(yīng)用戶事件,應(yīng)用也將表現(xiàn)為無響應(yīng)。
2.使用RAC-concat:解決
- (void)request4 {
// 創(chuàng)建一個(gè)信號(hào)管1
RACSignal *siganl1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
[subscriber sendNext:@"請求1結(jié)果"];
[subscriber sendCompleted];
NSLog(@"請求1-Success");
} failure:^(SPRequest *request, NSError *error) {
[subscriber sendCompleted];
NSLog(@"請求1-Fail");
}];
return [RACDisposable disposableWithBlock:^{}];
}];
// 創(chuàng)建一個(gè)信號(hào)管2
RACSignal *siganl2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
[subscriber sendNext:@"請求2結(jié)果"];
[subscriber sendCompleted];
NSLog(@"請求2-Success");
} failure:^(SPRequest *request, NSError *error) {
[subscriber sendCompleted];
NSLog(@"請求2-Fail");
}];
return [RACDisposable disposableWithBlock:^{}];
}];
// 創(chuàng)建一個(gè)信號(hào)管3
RACSignal *siganl3 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
[subscriber sendNext:@"請求3結(jié)果"];
[subscriber sendCompleted];
NSLog(@"請求3-Success");
} failure:^(SPRequest *request, NSError *error) {
[subscriber sendCompleted];
NSLog(@"請求3-Fail");
}];
return [RACDisposable disposableWithBlock:^{}];
}];
// 串聯(lián)管1管2管3
RACSignal *concatSiganl = [[siganl1 concat:siganl2] concat:siganl3];
//串聯(lián)后的接收端處理 ,兩個(gè)事件,走兩次,第一個(gè)打印siggnal1的結(jié)果,第二次打印siganl2的結(jié)果
[concatSiganl subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
打印結(jié)果:
2018-07-22 11:19:56.889535+0800 LPExample[7185:359636] 請求1結(jié)果
2018-07-22 11:19:56.890303+0800 LPExample[7185:359636] 請求1-Success
2018-07-22 11:19:57.102438+0800 LPExample[7185:359636] 請求2結(jié)果
2018-07-22 11:19:57.103094+0800 LPExample[7185:359636] 請求2-Success
2018-07-22 11:19:57.311119+0800 LPExample[7185:359636] 請求3結(jié)果
2018-07-22 11:19:57.311237+0800 LPExample[7185:359636] 請求3-Success
3.NSConditionLock解決
在所有回調(diào)加入到一個(gè)自定義并發(fā)隊(duì)列中,在隊(duì)列任務(wù)執(zhí)行前進(jìn)行條件加鎖。
- (void)request5 {
NSConditionLock *lock = [[NSConditionLock alloc] initWithCondition:0];
dispatch_queue_t queue = dispatch_queue_create("currentQueue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 3; i++) {
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
dispatch_async(queue, ^{
[lock lockWhenCondition:i];
NSLog(@"請求%@-Success",@(i));
[lock unlockWithCondition:i+1];
});
} failure:^(SPRequest *request, NSError *error) {
dispatch_async(queue, ^{
[lock lockWhenCondition:i];
NSLog(@"請求%@-Fail",@(i));
[lock unlockWithCondition:i+1];
});
}];
}
}
打印結(jié)果:
2018-07-22 11:44:50.595675+0800 LPExample[7479:377518] 請求0-Success
2018-07-22 11:44:50.595843+0800 LPExample[7479:377514] 請求1-Success
2018-07-22 11:44:50.644479+0800 LPExample[7479:377518] 請求2-Success
附件簡單AFNetworking封裝
#import <Foundation/Foundation.h>
#import <AFNetworking/AFNetworking.h>
@class SPRequest;
@protocol SPRequestDelegate <NSObject>
- (void)SPRequest:(SPRequest *)request finished:(NSDictionary *)response;
- (void)SPRequest:(SPRequest *)request Error:(NSError *)error;
@end
@interface SPRequest : NSObject
@property (assign) id <SPRequestDelegate> delegate;
/**
*[AFNetWorking]的operationManager對象
*/
@property (nonatomic, strong) AFHTTPSessionManager* operationManager;
/**
*當(dāng)前的請求operation隊(duì)列
*/
@property (nonatomic, strong) NSOperationQueue* operationQueue;
/**
*功能: 創(chuàng)建SPRequest的對象方法
*/
+ (instancetype)request;
/**
*功能:GET請求
*參數(shù):(1)請求的url: urlString
* (2)請求成功調(diào)用的Block: success
* (3)請求失敗調(diào)用的Block: failure
*/
- (void)GET:(NSString *)URLString
parameters:(NSDictionary*)parameters
success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
failure:(void (^)(SPRequest *request, NSError *error))failure;
/**
*功能:POST請求
*參數(shù):(1)請求的url: urlString
* (2)POST請求體參數(shù):parameters
* (3)請求成功調(diào)用的Block: success
* (4)請求失敗調(diào)用的Block: failure
*/
- (void)POST:(NSString *)URLString
parameters:(NSDictionary*)parameters
success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
failure:(void (^)(SPRequest *request, NSError *error))failure;
/**
* post請求
*
* @param URLString 請求網(wǎng)址
* @param parameters 請求參數(shù)
*/
- (void)postWithURL:(NSString *)URLString parameters:(NSDictionary *)parameters;
/**
* get 請求
*
* @param URLString 請求網(wǎng)址
*/
- (void)getWithURL:(NSString *)URLString;
/**
*取消當(dāng)前請求隊(duì)列的所有請求
*/
- (void)cancelAllOperations;
@end
#import "SPRequest.h"
@implementation SPRequest
+ (instancetype)request {
return [[self alloc] init];
}
- (instancetype)init {
self = [super init];
if (self) {
self.operationManager = [AFHTTPSessionManager manager];
self.operationQueue = self.operationManager.operationQueue;
self.operationManager.responseSerializer = [AFJSONResponseSerializer serializer];
self.operationManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/json", @"text/plain", @"text/html", nil];
}
return self;
}
- (void)GET:(NSString *)URLString
parameters:(NSDictionary*)parameters
success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
failure:(void (^)(SPRequest *request, NSError *error))failure {
[self.operationManager GET:URLString parameters:parameters progress:^(NSProgress * _Nonnull downloadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(self,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
failure(self,error);
}];
}
- (void)POST:(NSString *)URLString
parameters:(NSDictionary*)parameters
success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
failure:(void (^)(SPRequest *request, NSError *error))failure{
[self.operationManager POST:URLString parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
success(self,responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
failure(self,error);
}];
}
- (void)postWithURL:(NSString *)URLString parameters:(NSDictionary *)parameters {
[self POST:URLString
parameters:parameters
success:^(SPRequest *request, NSDictionary *responseObject) {
if ([self.delegate respondsToSelector:@selector(SPRequest:finished:)]) {
[self.delegate SPRequest:request finished:responseObject];
}
}
failure:^(SPRequest *request, NSError *error) {
if ([self.delegate respondsToSelector:@selector(SPRequest:Error:)]) {
[self.delegate SPRequest:request Error:error];
}
}];
}
- (void)getWithURL:(NSString *)URLString {
[self GET:URLString parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
if ([self.delegate respondsToSelector:@selector(SPRequest:finished:)]) {
[self.delegate SPRequest:request finished:responseObject];
}
} failure:^(SPRequest *request, NSError *error) {
if ([self.delegate respondsToSelector:@selector(SPRequest:Error:)]) {
[self.delegate SPRequest:request Error:error];
}
}];
}
- (void)cancelAllOperations{
[self.operationQueue cancelAllOperations];
}
@end