AFNetWorking 源码学习笔记 ☞ NSURLSessi
一、前言
本文是 AFNetWorking 源码学习笔记的第二篇,从本篇开始每次介绍四个部分中的一个,按顺序先介绍第一部分 -- NSURLSession 目录下的两个关键类 AFHTTPSessionManager 和 AFURLSessionManager。
注:标题容易让人误解,其实指的是 AFNetWorking 源码中的 NSURLSession 目录,而不是真的 NSURLSession 这个类。
AFNetWorking-NSURLSession.png二、正文
我们从整个框架对外的最直接接口 AFHTTPSessionManager.h 文件开始查看,此文件主要分三部分:
第一部分:4个基本属性:
①baseURL:顾名思义,基础的 URL ,对于同一个 App,通常情况下,请求 url 的前半部分都是相同的,将这一部分赋值给 baseURL,发送请求时就可以使用相对路径了。
②requestSerializer:请求的序列化类,负责拼接参数,及对参数的编码。
③responseSerializer:负责对请求结果的处理,
④securityPolicy:安全策略类,负责对服务器证书的内部验证,即在 AFNetWorking 内部先进行一次验证。
第二部分:创建及初始化方法
虽然提供了3种看似不同的创建及初始化方法(见下方代码),实际上前两者只是对后者不同程度的封装,最终都是调用了最后一个参数最多的初始化方法。
AFHTTPSessionManager.h
// 并非单例,从方法名就可以看出来
+ (instancetype)manager;
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
- (instancetype)initWithBaseURL:(nullable NSURL *)url
sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
AFHTTPSessionManager.m
// *** 初始化 最终都调用的是这个方法
- (instancetype)initWithBaseURL:(NSURL *)url
sessionConfiguration:(NSURLSessionConfiguration *)configuration
{
// 0.调用父类初始化方法
self = [super initWithSessionConfiguration:configuration];
if (!self) {
return nil;
}
// 1.调整 url, 确保 NSURL 的 +URLWithString:relativeToURL: 方法能够正常调用
if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) {
url = [url URLByAppendingPathComponent:@""];
}
// 2.初始化本类的属性
self.baseURL = url;
self.requestSerializer = [AFHTTPRequestSerializer serializer];
self.responseSerializer = [AFJSONResponseSerializer serializer];
return self;
}
第一步就是调用父类(AFURLSessionManager)的 initWithSessionConfiguration: 方法初始化,然后才是初始化自己的属性,点开父类方法看看。
// *** init 子类初始化时会通过 super 调用😎
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
// 初始化 configuration,如果为空,则使用默认,即 defaultSessionConfiguration,默认配置使用的是持久化的硬盘缓存,存储证书到用户 keychain,存储 cookie 到 shareCookie。
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
// 创建 delegate 所在的操作队列,因为 maxConcurrentOperationCount = 1,所以此处是串行队列,如果 > 1,则为并发队列。
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
/**
* 初始化 session
* 用于后边创建各种 task,这里设置了 delegate、operationQueue 和 sessionConfiguration
* 注意1: 这个 delegate 是 readonly,只能通过初始化方法设置;
* 注意2: self.operationQueue 默认是串行队列;
* 注意3: self.sessionConfiguration 默认取 defaultSessionConfiguration。
*/
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
// 用于对接口返回结果的处理 - 下一篇详细说明
self.responseSerializer = [AFJSONResponseSerializer serializer];
// 设置默认的安全策略类
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
// 如果是 Apple Watch,则不需要初始化 reachabilityManager
#if !TARGET_OS_WATCH
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
// 存放 task.idenfier 与其 delegate 组成的键值对,用于将系统提供的代理方法转发给其 delegate
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
// 初始化锁 🔐
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
/**
* 理论上,在初始化的时候,应该是还没有创建 task 的。
*
* 一个代码贡献者给出的解释是 I believe this for restoring a session from the background. 即 为从后台恢复一个 session 的情况准备的,将其代理的所有回调全都置为 nil。
* 详见:https://github.com/AFNetworking/AFNetworking/issues/3499
*/
[self.session getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task uploadProgress:nil downloadProgress:nil completionHandler:nil];
}
for (NSURLSessionUploadTask *uploadTask in uploadTasks) {
[self addDelegateForUploadTask:uploadTask progress:nil completionHandler:nil];
}
for (NSURLSessionDownloadTask *downloadTask in downloadTasks) {
[self addDelegateForDownloadTask:downloadTask progress:nil destination:nil completionHandler:nil];
}
}];
return self;
}
第三部分:核心 - 发送请求的方法
细数 AFHTTPSessionManager 中提供的一系列发送请求的方法,可以发现,其实主要可以分为两大类:一类是普通的 GET/POST/HEAD/PUT/PATCH/DELETE 方法,另一类是包含复合结构 request 的 POST 请求方法。
第一类方法
下面我们首先从第一类方法开始讨论,主要有以下几个方法,此处做了简化:
- (nullable NSURLSessionDataTask *)GET: parameters: success: failure: DEPRECATED_ATTRIBUTE
- (nullable NSURLSessionDataTask *)GET: parameters: progress: success: failure:
- (nullable NSURLSessionDataTask *)HEAD: parameters: success: failure:
- (nullable NSURLSessionDataTask *)POST: parameters: success: failure: DEPRECATED_ATTRIBUTE
- (nullable NSURLSessionDataTask *)POST: parameters: progress: success: failure:
- (nullable NSURLSessionDataTask *)PUT: parameters: success: failure:
- (nullable NSURLSessionDataTask *)PATCH: parameters: success: failure:
- (nullable NSURLSessionDataTask *)DELETE: parameters: success: failure:
这些方法的实现类似,都是调用了同一个方法创建 task,只不过传入的 method 不同而已,然后 resume 这个 task。如下边的这个 GET 方法所示,其他方法,只是将 GET 换成了对应 POST、HEAD 等等。
- (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
{
// 1.创建任务
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
// 2.启动任务
[dataTask resume];
return dataTask;
}
这个创建方法 dataTaskWithHTTPMethod: ... 最终做了 2 件事:
// *** 构建 NSURLSessionDataTask
- (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. 构建 NSMutableURLRequest,实际调用 requestSerializer 中创建 request 的方法
// 因为 NSURLRequest 的属性都是 readonly,所以此处构建了 NSMutableURLRequest。
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) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// 2. 构建 NSURLSessionDataTask:实际调用父类 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.调用 AFHTTPRequestSerializer 的 requestWithMethod: URLString: parameters: error: 方法创建 mutableURLRequest;
2.利用 父类即 AFURLSessionManager 的 dataTaskWithRequest: uploadProgress: downloadProgress: completionHandler: 创建 task(需要上一步得到的 mutableURLRequest 做参数)。
AFHTTPRequestSerializer 中的方法主要做了一些拼接参数及编码的工作,留待下一篇介绍。这里来看看 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 {
// 1.创建Task(同时修复iOS8以下系统出现的Bug)
__block NSURLSessionDataTask *dataTask = nil;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
// 2.为 task 添加代理
[self addDelegateForDataTask:dataTask
uploadProgress:uploadProgressBlock
downloadProgress:downloadProgressBlock
completionHandler:completionHandler];
return dataTask;
}
这里也做了 2 件事,见上边注释,不过,创建 dataTask 的时候使用了一个 block,点开定义发现,当 iOS 系统版本 < 8.0 时,创建了一个串行队列来执行 block 中的任务。
这是为了防止 iOS8 以前的版本在并发队列上创建任务时,可能会调用错误的 completionHandlers。当任务返回一个重复的 taskIdentifier 时,先前的 completionHandler 被清除并替换为新的。 如果第一个请求的数据在第二个请求的数据之前返回,那么将针对第二个 completionHandler 调用第一个响应。
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 about:https://github.com/AFNetworking/AFNetworking/issues/2093
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
static dispatch_queue_t url_session_manager_processing_queue() {
static dispatch_queue_t af_url_session_manager_processing_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_processing_queue = dispatch_queue_create("com.alamofire.networking.session.manager.processing", DISPATCH_QUEUE_CONCURRENT);
});
return af_url_session_manager_processing_queue;
}
第二类方法
第二类方法最终调用的只有一个方法,如下所示:
- (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
{
// 1.创建 request
NSError *serializationError = nil;
NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError];
if (serializationError) {
if (failure) {
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
// 2.创建 task
__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);
}
}
}];
// 3.启动任务
[task resume];
return task;
}
基本步骤与第一类方法类似,只不过创建 request 和 task 的方法不一样,对于 request 的创建方法留待下一篇介绍,这里我们看看创建 dataTask 的方法有什么不同。
- (NSURLSessionUploadTask *)uploadTaskWithStreamedRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
__block NSURLSessionUploadTask *uploadTask = nil;
url_session_manager_create_task_safely(^{
uploadTask = [self.session uploadTaskWithStreamedRequest:request];
});
[self addDelegateForUploadTask:uploadTask progress:uploadProgressBlock completionHandler:completionHandler];
return uploadTask;
}
原来是其中创建 task 的方法 uploadTaskWithStreamedRequest: 不同,生成的是 NSURLSessionUploadTask 类型,之后设置代理的方法与第一类方法类似。
注意:其实理论上应该还有第 3 类请求方法,即下载的请求,不过 AFHTTPSessionManager 里边并未封装,而只是在它的父类里边提供了创建 downloadTask 的方法,比如下边的。也是创建 task 使用的系统方法不同,其他类似。
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:(NSURLRequest *)request
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
__block NSURLSessionDownloadTask *downloadTask = nil;
url_session_manager_create_task_safely(^{
downloadTask = [self.session downloadTaskWithRequest:request];
});
[self addDelegateForDownloadTask:downloadTask progress:downloadProgressBlock destination:destination completionHandler:completionHandler];
return downloadTask;
}
现在分别介绍完了 2 类发送请求的方法,下边我们看看为 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] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
// 保存 task 和 delegate 的映射关系,并添加对任务开始和暂停的监听
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
这里创建了一个代理对象(AFURLSessionManagerTaskDelegate),在这个 task 中对请求过程进行处理;设置完代理的属性之后,才调用 setDelegate: forTask: 方法来为 task 及代理添加关联。
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
// 以 taskIdentifier 为 key,将 delegate 存储在一个字典(self.mutableTaskDelegatesKeyedByTaskIdentifier)里
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 添加对任务开始和暂停的监听
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
主要就是保存 task 和 代理之间的映射关系,保存在之前已经创建好的字典 mutableTaskDelegatesKeyedByTaskIdentifier 里边,并为 task 添加通知,监听了 task resume 和 suspend 两个状态。然后我们发现当状态发生改变的时候,实际上只是在主队列又发送了 2 中通知,只是改了个名字而已。全局搜索新的通知名后发现,只有在 UIKit 那个目录下的类里边才会用到。
然后看看代理类 AFURLSessionManagerTaskDelegate,从他的初始化方法开始。
- (instancetype)initWithTask:(NSURLSessionTask *)task {
self = [super init];
if (!self) {
return nil;
}
// 1.存放下载的数据
_mutableData = [NSMutableData data];
// 2.创建并初始化上传及下载进度 _uploadProgress 和 _downloadProgress
_uploadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
_downloadProgress = [[NSProgress alloc] initWithParent:nil userInfo:nil];
__weak __typeof__(task) weakTask = task;
for (NSProgress *progress in @[ _uploadProgress, _downloadProgress ])
{
progress.totalUnitCount = NSURLSessionTransferSizeUnknown;
progress.cancellable = YES; // 可以取消
progress.cancellationHandler = ^{
[weakTask cancel];
};
progress.pausable = YES; // 可以暂停
progress.pausingHandler = ^{
[weakTask suspend];
};
// 恢复
#if AF_CAN_USE_AT_AVAILABLE
if (@available(iOS 9, macOS 10.11, *))
#else
if ([progress respondsToSelector:@selector(setResumingHandler:)])
#endif
{
progress.resumingHandler = ^{
[weakTask resume];
};
}
// 给 progress 添加监听
[progress addObserver:self
forKeyPath:NSStringFromSelector(@selector(fractionCompleted))
options:NSKeyValueObservingOptionNew
context:NULL];
}
return self;
}
从上边的代码可以看到,就做了 2 件事:
- 创建存放下载的数据的可变数组 _mutableData;
- 创建用于存储上传及下载进度的对象 _uploadProgress 和 _downloadProgress,并为其添加 KVO 监听。
查看了 KVO 对应的observeValueForKeyPath:
方法实现后,发现只是将最新的下载或上传进度回传给了对应的 block。当然,会在 dealloc 方法中移除监听,否则会 Crash。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isEqual:self.downloadProgress]) { // 下载进度
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) { // 上传进度
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}
接下来就是一些代理方法了,他们都是从 AFURLSessionManager 里边的代理方法转发过来的,后边再作介绍吧。
回到 AFURLSessionManager.m 中,前边都是在请求发送之前做的准备工作,现在我们来看看发起请求之后的情况,当然是看代理方法了。
代理方法
这里的 15 个代理方法分属 4 个不同的协议:
NSURLSessionDelegate
NSURLSessionTaskDelegate
NSURLSessionDataDelegate
NSURLSessionDownloadDelegate
我们依次看看其中实现的协议方法:
NSURLSessionDelegate
// 当 session 失效时调用
- (void)URLSession:(NSURLSession *)session
didBecomeInvalidWithError:(NSError *)error
{
if (self.sessionDidBecomeInvalid) {
self.sessionDidBecomeInvalid(session, error);
}
[[NSNotificationCenter defaultCenter] postNotificationName:AFURLSessionDidInvalidateNotification object:session];
}
// 如何接受授权挑战,如果此方法未实现,会调用后边 NSURLSessionTaskDelegate 中的第一个代理方法 URLSession:task:willPerformHTTPRedirection:newRequest:completionHandler:
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
// 1.判断有没有自定义 block: sessionDidReceiveAuthenticationChallenge
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
// 2.如果没有自定义 block,判断 如果服务端要求的认证方法是不是 NSURLAuthenticationMethodServerTrust
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
// 3.基于客户端的安全策略来决定是否信任该服务器
// *** 点开此方法
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential; // 使用证书的 Challenge
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling; // 默认 Challenge
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge; // 取消 Challenge
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling; // 默认 Challenge
}
}
// *** 去执行 iOS 系统的认证方案
if (completionHandler) {
completionHandler(disposition, credential);
}
}
对于第一个方法 URLSession: didBecomeInvalidWithError:
,当 session 失效时调用此方法。官方文档给出的具体说明是,如果我们调用了 finishTasksAndInvalidate
方法,则会等到 session 里边最后一个 task 执行完,才会调用此代理方法;如果调用了 invalidateAndCancel
这个方法,则会立即执行该代理方法。
第 2 个代理方法是当客户端收到 “授权挑战”(姑且这么翻译吧,虽然很别扭) 时实行的方法,流程如下图:
AFNetWorking认证挑战.png-
首先,判断用户(使用者)有没有自定义 block: sessionDidReceiveAuthenticationChallenge,如果有,则执行自定义的 block 处理挑战,返回一个枚举值给
disposition
,并给credential
赋值 (因为参数是 &credential) 处理,他们都将作为最后一步执行 completionhandler() 时的参数。如果没有自定义处理挑战的 block,则接着进行下一步的判断; -
然后,判断服务端要求的认证方法是不是 NSURLAuthenticationMethodServerTrust。如果是,则只需要验证服务端证书是否安全(即 https 的单向认证,这是 AFNetworking 默认处理的认证方式,其他的认证方式,只能通过定义 block 来实现)。这种情况下,还需要做下一步处理;
-
接下来,有一个 if 判断,条件是
securityPolicy
调用evaluateServerTrust: forDomain:
方法的执行结果,该方法实际是 AFNetWorking 自己对先做的一次 HTTPS 认证,返回结果表明该服务器是否可以信任,具体方法实现见下一篇。- 如果 if 条件成立,即 AFNetworking 认为服务器可信任,执行
NSURLCredential
的系统方法credentialForTrust:
生成一个凭证或称证书credential
,如果成功生成,就给disposition
赋相应的值。 - 如果 if 条件不成立,则取消认证。
- 如果 if 条件成立,即 AFNetworking 认为服务器可信任,执行
-
最后,执行
completionHandler(disposition, credential)
,其中,credential
是根据服务端返回的证书及相关信息生成的用于客户端验证的对象;disposition
就是一个枚举值(常量),用于说明应该以何种方式处理凭证credential
。
NSURLSessionTaskDelegate
这里以典型的请求结束的代理方法为例做一个简单介绍,代码如下:
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
// 取出 task 对应的 delegate
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// 如果是在后台完成的任务,则此处 delegate 为 nil。
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
// 执行 block
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
这是在请求结束时执行的代理方法(可能成功或者失败,如果失败,error有值),调用自己的 delegateForTask:
方法,取出 task 对应的 delegate,如果取到了,则执行 delegate 的 URLSession: task: didCompleteWithError:
方法。
注意,实际我们最开始传到 GET、POST 等方法中的 block 都是赋值给了对应 task 的 delegate,所以真正执行那些 block 也是在转发到的 delegate(AFURLSessionManagerTaskDelegate) 的对应代理方法中,而不是上边代码最后的 block。
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
__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;
}
if (self.downloadFileURL) {
userInfo[AFNetworkingTaskDidCompleteAssetPathKey] = self.downloadFileURL;
} else if (data) {
userInfo[AFNetworkingTaskDidCompleteResponseDataKey] = data;
}
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;
// *** 使用 responseSerializer 处理返回结果
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];
});
});
});
}
}
仔细看了一下,这个方法里边就主要就为了做了 2 件事,第一件是执行 self.completionHandler(),包括请求结果的序列化将会在下一篇介绍;第二件是发送任务完成的通知,包括构建 userInfo 这个可变字典,详见代码。
至于其他协议及代理方法,大部分都是遵循将代理方法转发给 AFURLSessionManagerTaskDelegate 里的对应方法的逻辑,限于篇幅暂时就不做讨论了。
彩蛋
忽然想起来,其实 AFNetWorking 的 readme.md 里边给的 4 个示例都是直接使用 AFURLSessionManager 而不是使用它的子类 AFHTTPSessionManager,难道我又说多了,哈哈。