一. 前言
AFNetWorking的使用率非常高,这个不必多说。无论是AFNetWorking还是SDWebImage等涉及到网络请求的,底层都是封装了原生的网络请求。如果对原生的网络请求还不熟悉的话,可以看看我之前写的这篇文章【iOS小结】NSURLSession。
AFNetWorking的功能很多,我这篇主要解读的是网络请求(关于AFNetWorking怎么封装网络请求的知识)。
二. AFNetWorking的网络请求
网络请求涉及的类主要是这两个:
- AFHTTPSessionManager:拥有生成任务,管理任务的功能,并提供HTTP方法的API接口
- AFURLSessionManager:封装了生成任务,管理任务
我们先来看一个简单的POST请求,我们只需把urlString
和parameters
传进去,AFHTTPSessionManager就会创建一个POST的请求任务,并把请求进度,请求成功,请求失败通过Block回调给你。
简单来说,其内部的实现其实是会通过这两个参数生成对应的NSURLRequest,并通过NSURLSession创建请求任务,遵守相关的NSURLSession的协议,在对应的协议方法里面进行判断处理,回调。
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
//设置URL
NSString *urlString = @"www.baidu.com/login/info";
//设置参数
NSDictionary *parameters = @{@"username":@"Tom",@"pwd":@"123"};
//发送post请求
[manager POST:urlString parameters:parameters progress:^(NSProgress * _Nonnull uploadProgress) {
//上传过程中的回调
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
//成功的回调
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
//失败的回调
}];
接下来我们来一步步分析:
AFHTTPSessionManager
当使用AFNetWorking进行网络请求时,我们会创建AFHTTPSessionManager对象,进行对应的HTTP请求。AFHTTPSessionManager继承于AFURLSessionManager,除了AFURLSessionManager的功能外,还提供HTTP方法的接口。
以下红框是AFHTTPSessionManager提供的方法,以及对应的用途:
HTTP支持的方法.png
我们再来看具体提供的方法:
//GET方法
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
//GET方法(带下载进度)
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//HEAD方法
- (nullable NSURLSessionDataTask *)HEAD:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//POST方法
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
//POST方法(带上传进度)
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//POST方法(带构造体Block)→ 用于Multipart/form-data
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure DEPRECATED_ATTRIBUTE;
//POST方法(带构造体Block和上传进度)→ 用于Multipart/form-data
- (nullable NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(nullable id)parameters
constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//PUT方法
- (nullable NSURLSessionDataTask *)PUT:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//PATCH方法
- (nullable NSURLSessionDataTask *)PATCH:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
//DELETE方法
- (nullable NSURLSessionDataTask *)DELETE:(NSString *)URLString
parameters:(nullable id)parameters
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;
其中比较特殊的是POST方法,POST带构造体Block的方法是用于Multipart/form-data,用于上传文件。具体原理我就不多解释,可以看看这个例子AFNetworking文件上传和Multipart/form-data POST文件上传详解
。
另外,这两种方法创建的是uploadTask(通过文件流上传)。在下面方法中通过请求序列化对象self.requestSerializer
将url、参数和构造体Block序列化成对应的request,然后通过该request去创建对应的uploadTask。
// POST请求(带构造体block,带进度,用于Multipart/form-data,上传)
// 用于Multipart/form-data上传的基础方法
- (NSURLSessionDataTask *)POST:(NSString *)URLString
parameters:(id)parameters
constructingBodyWithBlock:(void (^)(id <AFMultipartFormData> formData))block
progress:(nullable void (^)(NSProgress * _Nonnull))uploadProgress
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
NSError *serializationError = nil;
//序列化request(构建)
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
//判断序列化是否成功
if (serializationError) {
//失败的话就通过failure回调,返回nil
if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
return nil;
}
//创建uploadTask(通过文件流上传文件)
__block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:uploadProgress completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(task, error);
}
} else {
if (success) {
success(task, responseObject);
}
}
}];
[task resume];
return task;
}
而其他方法创建的是dataTask。也是通过请求序列化对象self.requestSerializer
将url、参数序列化成对应的request,然后通过该request去创建对应的dataTask。
// 所有请求的基础方法(通过改变method参数)
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(void (^)(NSURLSessionDataTask *, id))success
failure:(void (^)(NSURLSessionDataTask *, NSError *))failure
{
NSError *serializationError = nil;
//把参数,还有各种东西转换为一个request
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//转换错误就在failure中回调
if (serializationError) {
if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//如果解析错误,直接返回
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
return nil;
}
//生成一个dataTask
__block NSURLSessionDataTask *dataTask = nil;
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
if (error) {
if (failure) {
failure(dataTask, error);
}
} else {
if (success) {
success(dataTask, responseObject);
}
}
}];
return dataTask;
}
总之,AFHTTPSessionManager封装了几种我们常用的网络请求以及文件上传的接口(当然也是不全的,比如少了downloadTask,这边就需要我们直接使用AFHTTPRequestSerializer去创建)。
通过AFHTTPRequestSerializer对象通过我们传入的参数转化成对应的NSMutableURLRequest。然后通过request调用其父类AFURLSessionManager创建对应的task。当然也把对应的success和failure等回调传进去。
另外,AFHTTPSessionManager有三个属性在初始化时会被创建,baseURL好理解,requestSerializer是用来序列化(构建)request的,
responseSerializer是用来处理响应数据的。requestSerializer是AFHTTPSessionManager特有的属性,因为AFHTTPSessionManager赋值构建request,然后生成对应的task,而responseSerializer实际上是用在AFURLSessionManager,因为它那边负责生成管理任务,并且对结果进行处理。
self.baseURL = url;
// 设置请求和响应序列化对象
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
AFURLSessionManager
其实从AFHTTPSessionManager先分析,再分析AFURLSessionManager是有弊端的,因为AFHTTPSessionManager继承于AFURLSessionManager,所以AFHTTPSessionManager的很多功能只有留到讲解AFURLSessionManager的时候才能说明。
之前AFHTTPSessionManager的分析说过,AFURLSessionManager封装了生成、管理任务。AFHTTPSessionManager只需要生成对应request,通过request调用AFURLSessionManager生成task的方法。
AFURLSessionManager是一个怎么样的类呢?
这边的探究分成两部分:
① 提供什么功能(其实还是跟任务有关)→看.h
② 怎么实现这些功能 →看.m
① AFURLSessionManager提供的功能
AFURLSessionManager提供的功能也是之前AFHTTPSessionManager未讲到的功能。AFURLSessionManager主要是封装了生成任务和管理任务,AFURLSessionManager实际上是NSURLSession对象的组合体,提供的功能自然也是围绕着任务。
AFURLSessionManager提供的功能主要有三种:
1)获取正在执行的所有任务、请求任务、上传任务、下载任务。
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
2)提供多种创建task的方法(封装了所有NSURLSession创建task的方法)
3)提供了对NSURLSession协议的自定义处理的Block。(类似于代理)
② AFURLSessionManager怎么实现这些功能
AFURLSessionManager.m的内容其实挺多的,分开来看其实主要有四部分内容(最主要还是第四部分):
1)单例创建各种队列的C函数。定义各种key,Notification的值。定义各种Block。(该部分也好理解,就是为了让下面来使用)
2)_AFURLSessionTaskSwizzling(主要用来交换NSURLSessionTask的Resume和Suspend方法)→主要是为了添加通知的代码?
+ (void)load {
/**
WARNING: 高能预警
https://github.com/AFNetworking/AFNetworking/pull/2702
*/
// 担心以后iOS中不存在NSURLSessionTask
if (NSClassFromString(@"NSURLSessionTask")) {
/**
iOS 7和iOS 8在NSURLSessionTask实现上有些许不同,这使得下面的代码实现略显trick
关于这个问题,大家做了很多Unit Test,足以证明这个方法是可行的
目前我们所知的:
- NSURLSessionTasks是一组class的统称,如果你仅仅使用提供的API来获取NSURLSessionTask的class,并不一定返回的是你想要的那个(获取NSURLSessionTask的class目的是为了获取其resume方法)
- 简单地使用[NSURLSessionTask class]并不起作用。你需要新建一个NSURLSession,并根据创建的session再构建出一个NSURLSessionTask对象才行。
- iOS 7上,localDataTask(下面代码构造出的NSURLSessionDataTask类型的变量,为了获取对应Class)的类型是 __NSCFLocalDataTask,__NSCFLocalDataTask继承自__NSCFLocalSessionTask,__NSCFLocalSessionTask继承自__NSCFURLSessionTask。
- iOS 8上,localDataTask的类型为__NSCFLocalDataTask,__NSCFLocalDataTask继承自__NSCFLocalSessionTask,__NSCFLocalSessionTask继承自NSURLSessionTask
- iOS 7上,__NSCFLocalSessionTask和__NSCFURLSessionTask是仅有的两个实现了resume和suspend方法的类,另外__NSCFLocalSessionTask中的resume和suspend并没有调用其父类(即__NSCFURLSessionTask)方法,这也意味着两个类的方法都需要进行method swizzling。
- iOS 8上,NSURLSessionTask是唯一实现了resume和suspend方法的类。这也意味着其是唯一需要进行method swizzling的类
- 因为NSURLSessionTask并不是在每个iOS版本中都存在,所以把这些放在此处(即load函数中),比如给一个dummy class添加swizzled方法都会变得很方便,管理起来也方便。
一些假设前提:
- 目前iOS中resume和suspend的方法实现中并没有调用对应的父类方法。如果日后iOS改变了这种做法,我们还需要重新处理
- 没有哪个后台task会重写resume和suspend函数
*/
// 1) 首先构建一个NSURLSession对象session,再通过session构建出一个_NSCFLocalDataTask变量
NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration ephemeralSessionConfiguration];
NSURLSession * session = [NSURLSession sessionWithConfiguration:configuration];
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
NSURLSessionDataTask *localDataTask = [session dataTaskWithURL:nil];
#pragma clang diagnostic pop
// 2) 获取到af_resume实现的指针
IMP originalAFResumeIMP = method_getImplementation(class_getInstanceMethod([self class], @selector(af_resume)));
Class currentClass = [localDataTask class];
// 3) 检查当前class是否实现了resume。如果实现了,继续第4步。
while (class_getInstanceMethod(currentClass, @selector(resume))) {
// 4) 获取到当前class的父类(superClass)
Class superClass = [currentClass superclass];
// 5) 获取到当前class对于resume实现的指针
IMP classResumeIMP = method_getImplementation(class_getInstanceMethod(currentClass, @selector(resume)));
// 6) 获取到父类对于resume实现的指针
IMP superclassResumeIMP = method_getImplementation(class_getInstanceMethod(superClass, @selector(resume)));
// 7) 如果当前class对于resume的实现和父类不一样(类似iOS7上的情况),并且当前class的resume实现和af_resume不一样,才进行method swizzling。
if (classResumeIMP != superclassResumeIMP &&
originalAFResumeIMP != classResumeIMP) {
[self swizzleResumeAndSuspendMethodForClass:currentClass];
}
// 8) 设置当前操作的class为其父类class,重复步骤3~8
currentClass = [currentClass superclass];
}
[localDataTask cancel];
[session finishTasksAndInvalidate];
}
}
+ (void)swizzleResumeAndSuspendMethodForClass:(Class)theClass {
Method afResumeMethod = class_getInstanceMethod(self, @selector(af_resume));
Method afSuspendMethod = class_getInstanceMethod(self, @selector(af_suspend));
if (af_addMethod(theClass, @selector(af_resume), afResumeMethod)) {
af_swizzleSelector(theClass, @selector(resume), @selector(af_resume));
}
if (af_addMethod(theClass, @selector(af_suspend), afSuspendMethod)) {
af_swizzleSelector(theClass, @selector(suspend), @selector(af_suspend));
}
}
- (NSURLSessionTaskState)state {
//在实际的虚拟类中不应调用状态方法。
NSAssert(NO, @"State method should never be called in the actual dummy class");
// 初始状态是NSURLSessionTaskStateCanceling;
return NSURLSessionTaskStateCanceling;
}
- (void)af_resume {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
//执行resume方法
[self af_resume];
//如果之前是其他状态,就变回resume状态,发出DidResume的通知
if (state != NSURLSessionTaskStateRunning) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidResumeNotification object:self];
}
}
- (void)af_suspend {
NSAssert([self respondsToSelector:@selector(state)], @"Does not respond to state");
NSURLSessionTaskState state = [self state];
//执行suspend方法
[self af_suspend];
//如果之前是其他状态,就变回suspend状态,发出DidSuspend的通知
if (state != NSURLSessionTaskStateSuspended) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFNSURLSessionTaskDidSuspendNotification object:self];
}
}
3)AFURLSessionManagerTaskDelegate(task代理类)
为什么是task的代理类呢?主要是因为task将它将处理获取数据,监听进度的职责放在该类去执行,AFURLSessionManagerTaskDelegate是task的代理人。我们先来看AFURLSessionManager里面的方法:(同理对应的uploadTask和downloadTask也有设置的方法)
//设置普通请求task的代理人
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
// 创建一个代理人
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//为task设置代理(把dataTask传给delegate)
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
可以说task该处理的回调都放在代理类这边处理了。我们再看看这个代理类怎么处理数据和监听进度。
关于进度的监听,AFURLSessionManagerTaskDelegate设置了两个NSProgress对象(uploadProgress和downloadProgress)来分别监听上传和下载进度。(关于NSProgress的作用可以看这篇文章进度: NSProgress)
简单说起来,NSProgress是一个记录进度的类,在需要记录多个进度有很大的优势。uploadProgress和downloadProgress除了记录进度外,通过设置也可以控制task的生命周期。
另外,通过对task接收到的字节数、期望接收到的字节数、发送的字节数、期望发送的字节数进行监听,值改变时去设置uploadProgress或downloadProgress对应的总值或完成值。通过对uploadProgress和downloadProgress完成度的监听,通过对应的uploadProgressBlock和downloadProgressBlock将进度信息回调出去。
//为task设置进度(task的进度会受到监听)
- (void)setupProgressForTask:(NSURLSessionTask *)task {
__weak __typeof__(task) weakTask = task;
//设置上传和下载进度条的总数目,暂停,取消,回复回调
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
self.downloadProgress.totalUnitCount = task.countOfBytesExpectedToReceive;
[self.uploadProgress setCancellable:YES];
[self.uploadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
[self.uploadProgress setPausable:YES];
[self.uploadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.uploadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}
[self.downloadProgress setCancellable:YES];
[self.downloadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
[self.downloadProgress setPausable:YES];
[self.downloadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
if ([self.downloadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.downloadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}
//对task接收到的字节数、期望接收到的字节数、发送的字节数、期望发送的字节数进行监听
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
options:NSKeyValueObservingOptionNew
context:NULL];
//对上传和下载完成的分数(NSProgress的fractionCompleted)进行监听
[self.downloadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
[self.uploadProgress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
//移除task的进度
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task {
//移除task接收到的字节数、期望接收到的字节数、发送的字节数、期望发送的字节数的监听
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))];
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))];
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))];
[task removeObserver:self forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))];
//移除上传,下载进度条完成的分数的监听
[self.downloadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
[self.uploadProgress removeObserver:self forKeyPath:NSStringFromSelector(@selector(fractionCompleted))];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
//如果是task的监听
if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
}
}
//如果是progress的监听
else if ([object isEqual:self.downloadProgress]) {
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
关于数据的处理(当然也包括对应成功失败的回调),主要是实现那几个有获取到数据的NSURLSession的代理方法,这几个代理方法是通过AFURLSessionManager来调用的,这个后续会提到。
关于数据最终的回调都是在- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
中处理,另外比如dowmloadTask还需要处理下载中拼接数据和下载完成移动文件夹的情况。
task完成时,除了将数据、响应、错误通过completionHandler回调出去,还发出DidComplete通知,传出去userInfo(保存着请求和响应序列化对象钝、数据、文件路径、错误信息等)。有一点特别的是completionHandler会在线程组中回调,我想这边是为了让开发者可以来设定监听。
#pragma mark - NSURLSessionTaskDelegate
//请求完成的回调
//处理完成后的数据
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
// 把响应序列化对象保存在userInfo
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
NSData *data = nil;
//把mutableData的值拿出来,并清空
if (self.mutableData) {
data = [self.mutableData copy];
//We no longer need the reference, so nil it out to gain back some memory.
self.mutableData = nil;
}
// 把下载路径和下载数据保存在userInfo
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
// 如果完成的回调有错误信息
if (error) {
// 把错误信息也保存在userInfo
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
//把所有完成的回调放在completionQueue的线程组中
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, error);
}
//在主线程发送任务完成的通知
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
} else {//没有错误信息
//在并行队列异步执行
dispatch_async(url_session_manager_processing_queue(), ^{
NSError *serializationError = nil;
//创建响应对象(解析成我们需要的数据)
responseObject = [manager.responseSerializer responseObjectForResponse:task.response data:data error:&serializationError];
//如果有下载路径就是下载,只要返回下载路径就可以
if (self.downloadFileURL) {
responseObject = self.downloadFileURL;
}
// 把响应对象保存在userInfo
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
// 把序列化错误信息保存在userInfo
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
//把所有完成的回调放在completionQueue的线程组中
dispatch_group_async(manager.completionGroup ?: url_session_manager_completion_group(), manager.completionQueue ?: dispatch_get_main_queue(), ^{
if (self.completionHandler) {
self.completionHandler(task.response, responseObject, serializationError);
}
//发出任务完成的通知(userInfo附带任务中的所有信息)
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
#pragma clang diagnostic pop
}
#pragma mark - NSURLSessionDataTaskDelegate
//开始受到数据的回调
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//拼接数据
[self.mutableData appendData:data];
}
#pragma mark - NSURLSessionDownloadTaskDelegate
//完成下载的回调
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
NSError *fileManagerError = nil;
self.downloadFileURL = nil;
if (self.downloadTaskDidFinishDownloading) {
//获取用户指定的文件储存的url地址
self.downloadFileURL = self.downloadTaskDidFinishDownloading(session, downloadTask, location);
if (self.downloadFileURL) {
//将储存在临时目录下的文件移动到用户指定的url地址
[[NSFileManager defaultManager] moveItemAtURL:location toURL:self.downloadFileURL error:&fileManagerError];
//如果移动失败就发出通知(移动失败了。。。)
if (fileManagerError) {
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDownloadTaskDidFailToMoveFileNotification object:downloadTask userInfo:fileManagerError.userInfo];
}
}
}
}
4)AFURLSessionManager(主要类,实现功能)
AFURLSessionManager包含一个NSURLSession属性,生成任务本质还是调用NSURLSession生成任务的方法,我们以创建dataTask为例,主要做了两个步骤,加锁通过session创建dataTask,为创建的dataTask设置代理人(AFURLSessionManagerTaskDelegate)。
#pragma mark - 普通请求任务
// 普通请求任务(带完成的回调)
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
return [self dataTaskWithRequest:request uploadProgress:nil downloadProgress:nil completionHandler:completionHandler];
}
// 普通请求任务(带完成的回调,以及上传进度和下载进度的回调)
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError * _Nullable error))completionHandler {
__block NSURLSessionDataTask *dataTask = nil;
//在串行队列中同步生成task(类似加锁)
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
//为生成的普通请求task添加代理
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
我们再来看看具体怎么设置代理人,这边的代理跟我们之前的代理协议啥的不一样,这边的代理人无非是AFURLSessionManager去调用代理类的方法(比如设置进度,添加通知,甚至后面的NSURLSession的代理方法)。一个任务对应一个代理类。另外,AFURLSessionManager还会将代理人保存在字典中,key是对应task的ID。
还有一点重要的是,为task添加通知(TaskDidResume和DidSuspend),这样就会剔除其他外来的任务,让该类产生的任务发出对应的通知。
//设置普通请求task的代理人
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
// 创建一个代理人
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//为task设置代理(把dataTask传给delegate)
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
//为task设置代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
// 断言判断参数是否为空
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
// 以task的id为key,AFURLSessionManagerTaskDelegate为value储存起来
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 代理人为task设置进度
[delegate setupProgressForTask:task];
//给task添加恢复和暂停的通知(AFNSURLSessionTaskDidResumeNotification和AFNSURLSessionTaskDidSuspendNotification)
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
那什么时候移除代理呢?那就是任务完成的时候。在这边我们也可以看到task完成的代理方法执行时,主动去获取对应task的代理,让他去执行对应操作。
//task完成时调用
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// 当在后台完成任务时,委托可能是nil
if (delegate) {
// 调用代理对应的方法(让代理去处理)
[delegate URLSession:session task:task didCompleteWithError:error];
// task完成了,移除代理
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
前面我们只是讲了生成dataTask,对应的还有downloadTask,uploadTask步骤也差不多(NSURLSession生成task,添加代理)。既然生成task,也遵守了其协议,肯定实现了NSURLSession的代理方法,就如上面提到的AFURLSessionManager提供了对NSURLSession协议的自定义处理的Block(类似于代理)。
这个意思是在代理方法中你可以选择默认的处理或自定义处理(实现自定义处理的Block)。以收到dataTask收到数据的代理方法为例,默认的处理是让代理类去处理,进行数据的拼接,我们也可以实现dataTaskDidReceiveData,自己处理(这种如果用代理实现应该好理解一点)。
//dataTask收到数据时调用
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//调用代理对应的方法(让代理去处理)
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
//自己处理
if (self.dataTaskDidReceiveData) {
self.dataTaskDidReceiveData(session, dataTask, data);
}
}
关于上面提到的(2)和(3)功能都说完了,我们来说说第一个功能(1)获取正在执行的所有任务、请求任务、上传任务、下载任务。其实就是使用session的getTasksWithCompletionHandler
方法。
#pragma mark - 获取当前运行的任务
- (NSArray *)tasksForKeyPath:(NSString *)keyPath {
__block NSArray *tasks = nil;
// 创建一个信号量,值为0
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(dataTasks))]) {
tasks = dataTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(uploadTasks))]) {
tasks = uploadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(downloadTasks))]) {
tasks = downloadTasks;
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(tasks))]) {
tasks = [@[dataTasks, uploadTasks, downloadTasks] valueForKeyPath:@"@unionOfArrays.self"];
}
//信号量加1
dispatch_semaphore_signal(semaphore);
}];
//一直等待,知道信号量不为0才会执行到这一步
//将计数值减1,并且执行返回
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
return tasks;
}
// 获取当前正在运行的tasks
- (NSArray *)tasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
// 获取当前正在运行的dataTasks
- (NSArray *)dataTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
// 获取当前正在运行的uploadTasks
- (NSArray *)uploadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
// 获取当前正在运行的downloadTasks
- (NSArray *)downloadTasks {
return [self tasksForKeyPath:NSStringFromSelector(_cmd)];
}
网友评论