導讀:AFN多請求依賴(一)
AFN多請求依賴(一)中解決的是如何將不同的AFN請求有序的進行請求:即第一個請求發(fā)出并且得到響應之后再次發(fā)出下一個請求并且得到響應,以此類推,最終在最后的一個請求得到響應之后執(zhí)行刷新UI等操作。那么一中用到的技術主要是信號量。
但是有一個問題這樣的異步AFN請求就相當于同步了,挨個挨個和主線程的同步沒什么區(qū)別,耗費時間比較長,那么我們現(xiàn)在有一個需求就是當所有的請求發(fā)出之后,只要響應都接收到了那么就刷新UI,不需要排隊挨個挨個去請求,但是一定要等到所有的請求請求到結果之后再刷新UI等操作,那么這個用到信號量肯定是不合適的,那么該怎么弄呢?
大家可以試一下其他的方式,比如說GCD的組之類的,但是我試過了不行,可能是我加的方式不對,大家有更好的方式還請簡書留言,本文中使用的是使用一個Flag來標志,每次發(fā)送請求的時候Flag值+1;得到響應之后Flag值建-1;當用KVO監(jiān)聽到值為0時,刷新UI等操作,并且移除監(jiān)聽。
工具準備
- 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
{
//創(chuàng)建請求管理者
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發(fā)送請求
[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);
}];
}
//監(jiān)聽
-(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(@"任務執(zhí)行完畢");
NSBlockOperation *blocOpMain = [NSBlockOperation blockOperationWithBlock:^{
//刷新UI
NSLog(@"刷新UI");
}];
[[NSOperationQueue mainQueue]addOperation:blocOpMain];
//移除觀察者
[self removeObserver:self forKeyPath:@"operationNum" context:nil];
}
}
}
@end
講解
工程大概是這樣的:
ViewController有一個屬性operationNum是執(zhí)行任務標記,我把它的值默認置為0,并且我把默認值設置放在KVO監(jiān)聽之前,并且監(jiān)聽的時候通過新值來判斷,是因為我用KVO監(jiān)聽的時候判斷的是0,如果等于0的話那一開始可能就已經會走一次了,所以我把設置operationNum放在KVO之前,到最后所有的任務都得到響應之后operationNum值就為0了,然后就會走監(jiān)聽方法。
@property(atomic,assign)NSInteger operationNum;
self.operationNum = 0;
[self addObserver:self forKeyPath:@"operationNum" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld context:nil];
我在ViewController.m上創(chuàng)建了一個按鈕,按鈕一點擊就會開始請求。每一個請求的開始的時候operationNum++;在響應代碼調用的時候operationNum--,具體代碼上面都有,這樣輸出來的效果如下:
如果有任何疑問或者改進的地方,還請大家簡書下評論,以上!