在APP开发中,我们经常会遇到下面两种需求:
1.同时做多个网络请求,所有网络请求都完成后进行下一步的操作。我们暂时称之为并发网路请求
2.依次做多个网络请求,最后一个网络请求完成后进行下一步的操作。我们暂时称之为依次网路请求
总结起来如下图
提供几种不同的解决方法
- 并发网络请求
解决方法
1.dispatch_group_t
解决 - 依次网络
解决方法
1.NSOperationQueue 线程依赖
+dispatch_semaphore_wait
解决
2.RAC-Concat
解决
3.NSConditionLock
解决
附件:简单AFNetworking封装
下面我们以实际例子作介绍。。。
请求路径
static NSString *url = @"https://api.douban.com/v2/book/1220562";
一、并发网络请求
1.使用dispatch_group_t
解决
1.1:不需要对每一次请求结果进行处理、只需知道什么视乎请求完成
- (void)request1 {
// 不需要对每一次请求结果进行处理、只需知道什么视乎请求完成
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(@"所有请求完成,进行下一步操作");
});
}
打印结果:
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] 所有请求完成,进行下一步操作
1.2: 需要对每一次请求结果进行处理、每个请求完成或者失败后,处理参数
- (void)request2 {
// 需要对每一次请求结果进行处理、每个请求完成或者失败后,处理参数
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进行处理
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
NSLog(@"请求1-Fail");
// 对请求1error进行处理
}];
});
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进行处理
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
NSLog(@"请求2-Fail");
// 对请求2error进行处理
}];
});
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进行处理
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
NSLog(@"请求3-Fail");
// 对请求3error进行处理
}];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"所有请求完成,进行下一步操作");
});
}
打印结果:
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] 所有请求完成,进行下一步操作
使用dispatch_group_enter(group)
和dispatch_group_leave(group)
需注意:
enter和leave必须配合使用,有几次enter就要有几次leave,否则group会一直存在。当所有enter的block都leave后,会执行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的结果
} else if (idx == 1) {
//请求1的结果
} else if (idx == 2) {
//请求2的结果
}
} failure:^(SPRequest *request, NSError *error) {
dispatch_group_leave(group);
if (idx == 0) {
//请求0的结果
} else if (idx == 1) {
//请求1的结果
} else if (idx == 2) {
//请求2的结果
}
}];
});
}];
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"\n所有请求完成,进行下一步操作");
});
}
二、 依次网络解决方法
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.设置依赖
[operation2 addDependency:operation1]; //请求2依赖请求1
[operation3 addDependency:operation2]; //请求3依赖请求2
// 5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
}
打印结果:
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
注意:
绝对不要在应用主线程中等待一个Operation,只能在第二或次要线程中等待。阻塞主线程将导致应用无法响应用户事件,应用也将表现为无响应。
2.使用RAC-concat:
解决
- (void)request4 {
// 创建一个信号管1
RACSignal *siganl1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
[subscriber sendNext:@"请求1结果"];
[subscriber sendCompleted];
NSLog(@"请求1-Success");
} failure:^(SPRequest *request, NSError *error) {
[subscriber sendCompleted];
NSLog(@"请求1-Fail");
}];
return [RACDisposable disposableWithBlock:^{}];
}];
// 创建一个信号管2
RACSignal *siganl2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
[subscriber sendNext:@"请求2结果"];
[subscriber sendCompleted];
NSLog(@"请求2-Success");
} failure:^(SPRequest *request, NSError *error) {
[subscriber sendCompleted];
NSLog(@"请求2-Fail");
}];
return [RACDisposable disposableWithBlock:^{}];
}];
// 创建一个信号管3
RACSignal *siganl3 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[SPRequest request] GET:url parameters:nil success:^(SPRequest *request, NSDictionary *responseObject) {
[subscriber sendNext:@"请求3结果"];
[subscriber sendCompleted];
NSLog(@"请求3-Success");
} failure:^(SPRequest *request, NSError *error) {
[subscriber sendCompleted];
NSLog(@"请求3-Fail");
}];
return [RACDisposable disposableWithBlock:^{}];
}];
// 串联管1管2管3
RACSignal *concatSiganl = [[siganl1 concat:siganl2] concat:siganl3];
//串联后的接收端处理 ,两个事件,走两次,第一个打印siggnal1的结果,第二次打印siganl2的结果
[concatSiganl subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
}
打印结果:
2018-07-22 11:19:56.889535+0800 LPExample[7185:359636] 请求1结果
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结果
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结果
2018-07-22 11:19:57.311237+0800 LPExample[7185:359636] 请求3-Success
3.NSConditionLock
解决
在所有回调加入到一个自定义并发队列中,在队列任务执行前进行条件加锁。
- (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];
});
}];
}
}
打印结果:
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;
/**
*当前的请求operation队列
*/
@property (nonatomic, strong) NSOperationQueue* operationQueue;
/**
*功能: 创建SPRequest的对象方法
*/
+ (instancetype)request;
/**
*功能:GET请求
*参数:(1)请求的url: urlString
* (2)请求成功调用的Block: success
* (3)请求失败调用的Block: failure
*/
- (void)GET:(NSString *)URLString
parameters:(NSDictionary*)parameters
success:(void (^)(SPRequest *request, NSDictionary *responseObject))success
failure:(void (^)(SPRequest *request, NSError *error))failure;
/**
*功能:POST请求
*参数:(1)请求的url: urlString
* (2)POST请求体参数:parameters
* (3)请求成功调用的Block: success
* (4)请求失败调用的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 请求网址
* @param parameters 请求参数
*/
- (void)postWithURL:(NSString *)URLString parameters:(NSDictionary *)parameters;
/**
* get 请求
*
* @param URLString 请求网址
*/
- (void)getWithURL:(NSString *)URLString;
/**
*取消当前请求队列的所有请求
*/
- (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