一.背景
之前没怎么太过关注AFN的源码,最近花了一点时间读了一下,感觉获益匪浅,也学到了不少的小技巧.这里不探讨具体的使用,重点在于请求过程以及一些注意点.
二.AFN的网络请求过程
1.这里先要介绍几个类
AFHTTPSessionManager
:这个类就是我们经常使用的类了,我们就是通过这个类完成了请求任务.
AFURLSessionManager
:AFHTTPSessionManager继承自AFURLSessionManager,它内部封装了NSURLSession的delegate方法,是最基本的请求类.
AFURLSessionManagerTaskDelegate
:这是内置于AFURLSessionManager的一个类,它实现了NSURLSession请求的数据转发和回调.
2.过程分析
AFHTTPSessionManager
分析之前,我们先看看AFHTTPSessionManager
的初始化过程,聚焦到其中这个最底层的初始化方法:
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
self.baseURL = url;
//这里有默认解析方式
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
所以如果我们不设置AFHTTPSessionManager
的解析方式,那么默认是AFHTTPRequestSerializer
,AFJSONResponseSerializer
.我看到某些项目中还在外部设置了解析方式,如果和默认的一样,其实可以省略.
以GET请求为例
当我们command + 左键
进入GET请求方法内时,可以看到有如下两个API:
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
{
return [self GET:URLString parameters:parameters progress:nil success:success failure:failure];
}
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
我们可以发现
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
这个API底层调用的是:
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
progress:(void (^)(NSProgress * _Nonnull))downloadProgress
success:(void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
他们之间的区别后者在于多了一个progress
回调.这个progress
回调是用来回调下载进度的.
然后就到了不管是GET,POST等都会调用的底层方法:
- (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{
//(1)判断解析是否成功
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//如果解析错误直接返回
if (serializationError) {
if (failure) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
//(2)如果请求中用异步线程请求,完成就用主线程进行回调.
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
#pragma clang diagnostic pop
}
return nil;
}
//(3).调用父类AFURLSessionManager的方法进行数据回调
__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;
}
在这个方法中我们可以看到如下几点:
(1).先判断解析是否成功,成功之后才会继续请求.
(2).然后利用
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
这种形式进行数据回调,如果我们有自定义的队列,这里就是completionQueue
,那么就用它进行回调,没有就放在主线程回调.
(3).这个方法调用了父类AFURLSessionManager
- (NSURLSessionDataTask *)dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error)
那么接下来就到AFURLSessionManager
了.
AFURLSessionManager
这里定位到如下代码:
- (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;
//(1)安全的创建一个dataTask
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
//(2)将task进行转发,得到请求完成的回调
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
这里我们有两点需要注意的:
(1).url_session_manager_create_task_safely
是为了防止在iOS8之前的一个bug,当重复创建task时,新建的task可能会把之前的task覆盖,造成数据回调异常.所以这里抛了一个队列url_session_manager_creation_queue
用来解决这个问题.
#define NSFoundationVersionNumber_With_Fixed_5871104061079552_bug NSFoundationVersionNumber_iOS_8_0
......
static void url_session_manager_create_task_safely(dispatch_block_t block) {
if (NSFoundationVersionNumber < NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
// Fix of bug
// Open Radar:http://openradar.appspot.com/radar?id=5871104061079552 (status: Fixed in iOS8)
// Issue 这里是bug地址 about:https://github.com/AFNetworking/AFNetworking/issues/2093
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
不过这个问题在iOS8
已经以及修复了.
(2)这里用了一个addDelegateForDataTask
方法来接收这个task.目的是转发这个task到AFURLSessionManagerTaskDelegate
中,让这个类完成最后的数据转发,并回调.
到这里需要中断一下,因为有个疑问,为什么AFN
要设计这样一个类来完成请求任务?我们先看看AFURLSessionManagerTaskDelegate
这个类.
AFURLSessionManagerTaskDelegate
先看它的声明:
@interface AFURLSessionManagerTaskDelegate : NSObject
@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSProgress *uploadProgress;
@property (nonatomic, strong) NSProgress *downloadProgress;
@property (nonatomic, copy) NSURL *downloadFileURL;
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
@end
从中我们可以看出如下几点:
(1).它遵守了NSURLSessionTaskDelegate
, NSURLSessionDataDelegate
, NSURLSessionDownloadDelegate
这三个协议,并且实现了这三个协议方法.
(2).它持有了AFURLSessionManager
,并且用weak
进行修饰.并且定义了如下属性:
mutableData:获取的数据data.
uploadProgress:上传的进度.
downloadProgress:下载的进度.
downloadFileURL:下载的文件的URL.
downloadTaskDidFinishDownloading:完成下载的block.
uploadProgressBlock:回调上传进度的block.
downloadProgressBlock:回调下载进度的block.
completionHandler:任务完成的回调,从这个block的定义可以看出,就是这个block回调了我们需要的responseObject
,error
.
typedef void (^AFURLSessionTaskCompletionHandler)(NSURLResponse *response, id responseObject, NSError *error);
这里就大概能猜测出AFURLSessionManagerTaskDelegate
的作用就是管理进度和完成数据的转发和回调.
这里关于上传和下载进度管理进行略过...主要看一下网络数据是怎么来和回调的.
我们可以发现下面实现了三个NSURLSession的代理方法:
//完成数据转发和回调给上层
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error;
//获取下载的数据
- (void)URLSession:(__unused NSURLSession *)session
dataTask:(__unused NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data;
//完成下载任务时,获取下载的文件地址,并且将文件写入到改地址中.
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location;
这里先暂告一段落,我们跳回到AFURLSessionManager
,实际上完成请求任务的还是AFURLSessionManager
.
看它的初始化方法中的这一句代码:
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
说明底层是通过NSURLSession
的delegate这种方式来进行请求.
我们忽略其他处理,又回到addDelegateForDataTask
这个方法:
- (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
{
//生成一个delegate
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] init];
//持有当前的这个AFURLSessionManager,这里决定了为啥manager要用weak修饰.
delegate.manager = self;
//完成请求回调的block
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//将这个task进行转发,让这个delegate去处理task.
[self setDelegate:delegate forTask:dataTask];
//上传和下载的进度
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
在setDelegate
中:
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
//加锁
[self.lock lock];
//保存这个delegate
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
//解锁
[self.lock unlock];
}
同样还有一个delegateForTask
方法:
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
//加锁
[self.lock lock];
//得到保存的delegate
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
//解锁
[self.lock unlock];
return delegate;
}
这里有加锁的目的是为了防止多个请求存在时,造成delegate存取冲突.
然后直接跳入相关delegate方法中去:
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//(1)获取当前任务的处理者
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
//(2)这里delegate可能为空
if (delegate) {
//(3)转发task
[delegate URLSession:session task:task didCompleteWithError:error];
//(4)完成请求移除task
[self removeDelegateForTask:task];
}
//(5)任务完成
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
前面说过AFURLSessionManagerTaskDelegate
已经实现了NSURLSessionDataTaskDelegate
的三个方法,所以这里(3)实现了转发.这里再回到NSURLSessionDataTaskDelegate
的这个代理方法中:
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//这里的task就是从前面转发过来的task,里面包含了下载的结果
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
__strong AFURLSessionManager *manager = self.manager;
__block id responseObject = nil;
//这个字典保存了下载的一些信息,用于通知转发.
__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
//Performance Improvement from #2672
//接收下载的数据
NSData *data = nil;
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;
}
//保存下载完成的URL和data
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
//如果error存在
if (error) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = error;
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;
}
if (responseObject) {
userInfo[AFNetworkingTaskDidCompleteSerializedResponseKey] = responseObject;
}
if (serializationError) {
userInfo[AFNetworkingTaskDidCompleteErrorKey] = serializationError;
}
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);
}
dispatch_async(dispatch_get_main_queue(), ^{
[[NSNotificationCenter defaultCenter] postNotificationName:AFNetworkingTaskDidCompleteNotification object:task userInfo:userInfo];
});
});
});
}
#pragma clang diagnostic pop
}
这里就是我们请求的数据最开始的出口了.所以到这里,我们的一个普通的请求过程就完成了.总结一下大概过程:
app->AFHTTPSessionManager->AFURLSessionManager->AFURLSessionManagerTaskDelegate
->AFURLSessionManager->AFHTTPSessionManager->app
到此为止,AFN请求过程就有一个基本的概念了,至于其他模块,暂时不去讨论.