AFN多请求依赖(二)
导读:AFN多请求依赖(一)
AFN多请求依赖(一)中解决的是如何将不同的AFN请求有序的进行请求:即第一个请求发出并且得到响应之后再次发出下一个请求并且得到响应,以此类推,最终在最后的一个请求得到响应之后执行刷新UI等操作。那么一中用到的技术主要是信号量。
但是有一个问题这样的异步AFN请求就相当于同步了,挨个挨个和主线程的同步没什么区别,耗费时间比较长,那么我们现在有一个需求就是当所有的请求发出之后,只要响应都接收到了那么就刷新UI,不需要排队挨个挨个去请求,但是一定要等到所有的请求请求到结果之后再刷新UI等操作,那么这个用到信号量肯定是不合适的,那么该怎么弄呢?
大家可以试一下其他的方式,比如说GCD的组之类的,但是我试过了不行,可能是我加的方式不对,大家有更好的方式还请简书留言,本文中使用的是使用一个Flag来标志,每次发送请求的时候Flag值+1;得到响应之后Flag值建-1;当用KVO监听到值为0时,刷新UI等操作,并且移除监听。
工具准备
- AFN:这个可以手动导入也可以通过Cocoapods
- BasicAFNTool:基于AFN封装的网络请求工具类
//BasicAFNTool.h
#import <Foundation/Foundation.h>
@interface BasicAFNTool : NSObject
+ (void)get:(NSString *)url
params:(NSDictionary *)params
success:(void(^)(id json))success
failure:(void(^)(NSError *error))failure;
+ (void)post:(NSString *)url
params:(NSDictionary *)params
success:(void(^)(id json))success
failure:(void(^)(NSError *error))failure;
@end
//BasicAFNTool.m
#import "BasicAFNTool.h"
#import "AFNetworking.h"
@implementation BasicAFNTool
+ (void)get:(NSString *)url
params:(NSDictionary *)params
success:(void(^)(id json))success
failure:(void(^)(NSError *error))failure
{
//创建请求管理者
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
//[manager.requestSerializer setValue:@"2" forHTTPHeaderField:@"User_Agent"];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/html",@"text/plain",@"image/jpeg", nil];
//2发送请求
[manager GET:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success) success(responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure) failure(error);
}];
}
+ (void)post:(NSString *)url
params:(NSDictionary *)params
success:(void(^)(id json))success
failure:(void(^)(NSError *error))failure
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//[manager.requestSerializer setValue:@"2" forHTTPHeaderField:@"User_Agent"];
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json",@"text/html",@"text/plain", nil];
[manager POST:url parameters:params progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (success) success(responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (failure) failure(error);
}];
}
@end
- KODURLService:URL链接的工具类
//KODURLService.h
#import <Foundation/Foundation.h>
@interface KODURLService : NSObject
+ (NSString *)urlString1;
+ (NSString *)urlString2;
+ (NSString *)urlString3;
@end
//KODURLService.m
#import "KODURLService.h"
@implementation KODURLService
+ (instancetype)defaultService {
static KODURLService *instance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [[self alloc] init];
});
return instance;
}
+ (NSString *)urlString1{
NSString *urlString1 = @"https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=3495161387,2602242859&fm=23&gp=0.jpg";
//NSString *urlString1 = @"http://github.com/qiruihua/bigimage/raw/master/011.jpg";
return urlString1;
}
+ (NSString *)urlString2{
NSString *urlString2 = @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493299941944&di=979daec634bebdcbb3c14792a2f1fe83&imgtype=0&src=http%3A%2F%2Fimg.taopic.com%2Fuploads%2Fallimg%2F121209%2F234928-12120Z0543764.jpg";
return urlString2;
}
+ (NSString *)urlString3{
NSString *urlString3 = @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1493299941944&di=76092dbeceb09618622fd7993510c9fb&imgtype=0&src=http%3A%2F%2Fmvimg1.meitudata.com%2F55e2b8683d7464031.jpg";
return urlString3;
}
@end
开干
#import "ViewController.h"
#import "KODURLService.h"
#import "BasicAFNTool.h"
@interface ViewController ()
@property(atomic,assign)NSInteger operationNum;//队列标识记录
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.operationNum = 0;
[self addObserver:self forKeyPath:@"operationNum" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
[self addBtn2];
}
- (void)addBtn2{
UIButton *btn = [[UIButton alloc]init];
btn.frame = CGRectMake(0, 300, 200, 50);
CGPoint btnCenter = CGPointMake(self.view.center.x, btn.frame.origin.y + 25);
btn.center = btnCenter;
btn.backgroundColor = [UIColor orangeColor];
[self.view addSubview:btn];
[btn setTitle:@"外部操作封GCD信号" forState:UIControlStateNormal];
[btn addTarget:self action:@selector(didClickBtn2) forControlEvents:UIControlEventTouchUpInside];
}
- (void)didClickBtn2{
NSBlockOperation *blockOp1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"正在开始下载图片11111...");
self.operationNum ++;
[self basicAFNRequestWithUrlString:[KODURLService urlString1] message:@"dispatch_group_enter请求1" success:^(id json){
NSLog(@"最终成功完成了请求1");
self.operationNum --;
} error:^(NSError *error){
NSLog(@"最终失败完成了请求1,error = %@",error);
self.operationNum --;
}];
}];
NSBlockOperation *blockOp2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"正在开始下载图片22222...");
self.operationNum ++;
[self basicAFNRequestWithUrlString:[KODURLService urlString2] message:@"dispatch_group_enter请求2" success:^(id json){
NSLog(@"最终成功完成了请求2");
self.operationNum --;
} error:^(NSError *error){
NSLog(@"最终失败完成了请求2,error = %@",error);
self.operationNum --;
}];
}];
NSBlockOperation *blockOp3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"正在开始下载图片33333...");
self.operationNum ++;
[self basicAFNRequestWithUrlString:[KODURLService urlString3] message:@"dispatch_group_enter请求3" success:^(id json){
NSLog(@"最终成功完成了请求3");
self.operationNum --;
} error:^(NSError *error){
NSLog(@"最终失败完成了请求3,error = %@",error);
self.operationNum --;
}];
}];
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
[queue setMaxConcurrentOperationCount:3];
[blockOp2 addDependency:blockOp1];
[blockOp3 addDependency:blockOp2];
[queue addOperations:@[blockOp1,blockOp2,blockOp3] waitUntilFinished:NO];
}
#pragma mark - private
- (void)basicAFNRequestWithUrlString:(NSString *)urlString
message:(NSString *)message
success:(void(^)(id json))success
error:(void(^)(NSError *error))errorBlock
{
NSLog(@"需要拿参%@",message);
[BasicAFNTool get:urlString params:nil success:^(id json) {
NSLog(@"%@",message);
success(json);
} failure:^(NSError *error) {
NSLog(@"%@",message);
errorBlock(error);
}];
}
//监听
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
if([keyPath isEqualToString:@"operationNum"] && object == self)
{
NSInteger new = [change[NSKeyValueChangeNewKey] integerValue];
if (new == 0) {
NSLog(@"任务执行完毕");
NSBlockOperation *blocOpMain = [NSBlockOperation blockOperationWithBlock:^{
//刷新UI
NSLog(@"刷新UI");
}];
[[NSOperationQueue mainQueue]addOperation:blocOpMain];
//移除观察者
[self removeObserver:self forKeyPath:@"operationNum" context:nil];
}
}
}
@end
讲解
工程大概是这样的:
ViewController有一个属性operationNum是执行任务标记,我把它的值默认置为0,并且我把默认值设置放在KVO监听之前,并且监听的时候通过新值来判断,是因为我用KVO监听的时候判断的是0,如果等于0的话那一开始可能就已经会走一次了,所以我把设置operationNum放在KVO之前,到最后所有的任务都得到响应之后operationNum值就为0了,然后就会走监听方法。
@property(atomic,assign)NSInteger operationNum;
self.operationNum = 0;
[self addObserver:self forKeyPath:@"operationNum" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
我在ViewController.m上创建了一个按钮,按钮一点击就会开始请求。每一个请求的开始的时候operationNum++;在响应代码调用的时候operationNum--,具体代码上面都有,这样输出来的效果如下:
Snip20170704_133.png如果有任何疑问或者改进的地方,还请大家简书下评论,以上!
逗比.gif