现阶段几乎所有的App都有联网的需求,而AFNetworking则是一款优秀的HTTP网络通信库,Github上star数已过2W可以说是变成了iOS App标配三方库。AFNetworking3.0以前是基于NSURLConnetion,3.0以后才用Apple最新的网络库NSURLSession,本文仅用于记录学习AFNeworking3.1源码的心得。
AFNetworking3.1代码结构非常清晰,通过CocoaPods下载源码后可以看出主要由4部分组成:NSURLSession、Reachability、Security、Serialization。本篇先学习NSURLSession。

Session
AFNetworking中与Session相关的有2个类,分别为AFHTTPSessionManager和AFURLSessionManger。其中AFHTTPSessionManager是AFURLSessionManager的子类,主要提供了一些方便生成HTTP请求的接口;AFURLSessionManager则用于创建和管理NSURLSession对象。如果项目中与WebService交互的特别频繁,那么建议子类化AFHTTPSessionManager然后提供一个类方法来返回一个单例对象,该单例对象配置了项目中通用的鉴权、缓存之类的配置,此方法还可以统一处理异常行为。
初始化
- NSURLSession配置:NSURLSession默认配置为
defaultSessionConfiguration,该配置可以共享全局认证、缓存、Cookie存储策略。 - 解析线程:创建了一个串行的
NSOperationQueue用于处理Session的回调。 - 多线程安全:为了使Add Request和Remove Request线程安全,创建了一个互斥锁
NSLock *lock。 - 响应格式:默认是
JSON格式。 - 安全策略:默认是
AFSSLPinningModeNone。
核心组件通信
一条HTTP请求创建、发送、响应接收需用到NSURLSession和Serialization这2个核心组件,整个通信过程如下图所示。
AFURLSessionManagerTaskDelegate是AFURLSessionManager的委托,处理实现请求进度、响应回调等与业务层直接交互的接口。AFURLSessionManager直接实现NSURLSessionDelegate系列回调接口,在实现
- (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
这几个接口时委托给AFURLSessionManagerTaskDelegate处理。
关键技术
响应->请求映射
一般编写的网络请求都是异步的,当发送了多条网络请求后,如何识别出接收到的响应是哪个请求的结果呢?AFNetworking的做法是在内部维护一个NSMutableDictionary,key是NSURLSessionTask的taskIdentifier(由NSURLSession框架维护,确保唯一),value是AFURLSessionManagerTaskDelegate(该委托持有请求的completionHandler、progress等),在发送请求时添加taskIdentifier和相应的AFURLSessionManagerTaskDelegate,所以当AFNetworking接收到NSURLSession的finish回调时通过taskIdentifier查找到相应的AFURLSessionManagerTaskDelegate,然后AFURLSessionManagerTaskDelegate根据responseSerialization解析出响应数据返回给回调者。
说到这儿你可能会疑惑taskIdentifier是如何确定的,更底层的实现是如何的呢?想要弄明白这些事情我们先补充点TCP相关的知识(HTTP是建立在TCP传输层之上的),一条TCP连接需要由6个元素确定:源mac地址、源ip地址、源端口号、目标mac地址、目标ip地址、目标端口号。当我们在一个App中向同一个Web服务器发送多条请求(即便是同一个Request发送了多次)时,就会创建多条TCP连接,这些连接的区别只在于源端口号不同,而源端口号默认是内核分配的,内核是知道哪个App(precess)申请了创建了哪些TCP连接的,所以当创建一个NSURLSessionTask时内核会先创建一条tcp连接,然后根据相关策略生成一个类端口号的标示返回赋值给taskIdentifier。大体意思如下图所示。
因为可以确定发送请求时的tcp端口号,所以响应到来时根据tcp端口号就可以确定是哪个请求的响应了。可以看出图中的ResponseA是Request2的响应,ResponseB是Request1的响应。
总而言之就是每条Http请求都有自己的tcp连接,相当于有独立的传输通道,所以就可以根据响应的通道标示来找出是哪个请求的响应。
请求进度progress实现
请求progress主要由AFURLSessionManagerTaskDelegate类实现,该类有两个NSProgress属性,分别为uploadProgress和downloadProgress,在创建请求时AFURLSessionManagerTaskDelegate让uploadProgress和downloadProgress使用KVO监听了NSURLSessionTask的属性(countOfBytesReceived、countOfBytesExpectedToReceive、countOfBytesSent、countOfBytesExpectedToSend),所以在接收到请求数据后progress能实时响应。
三条线程
处理系统网络响应数据线程:NSOperationQueue创建,NSOperationQueue是在初始化AFURLSessionManager时创建的。
解析ResponseData线程:url_session_manager_processing_queue,AFNetworking私有队列,并行队列。
回调业务请求线程:于业务completionQueue一致,如果没指定则为主线程。
__block修饰符
__block表示为引用传递,共享内存,在AFNetworking中有如下代码:
__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);
}
}
}];
如果上述block NSURLSessionDataTask *dataTask = nil没用block修饰的话,则failure(dataTask, error)和success(dataTask, responseObject)中dataTask始终为nil,因为block捕获了dataTask = nil不会再改变,根本原因是block默认是值拷贝,所以此处需要增加__block。