AFNetworking是iOS平台(实际上它也支持OS X,watch OS)里做网络框架最出名的那个http网络层框架。接口封装良好,支持各种标准的http请求,更新及时,github上2W+的star,几乎都要成iOS的业界标准了。一个http请求发出去,拿到一个字典对象(如果是json序列化),然后取出来在成功回调里各种刷UI或者该干嘛干嘛,多开心啊_,你问我滋磁不滋磁,我当然是滋磁滴。本系列将基于以下模块(按AF的文件夹分的)在做AF的源码解析,话不多说直接上正题:
AFNetworking里分为以下几部分:
- NSURLSession。核心代码,涉及客户端发起网络请求到收到响应数据的全过程处理。请求的数据处理以及响应的数据处理在放在序列化那块。
- Reachability。检查网络状况
- Security。设计网络安全部分,主要用于https中防中间人攻击
- Serialization。序列化,核心代码。主要涉及到构造网络请求以及对拿到的网络请求数据做一些处理。
- UIKit。主要是对一些常用UI控件做网络交互方便的扩展。
NSURLSession模块主要包括以下两个类:
AFHttpSessionManager和AFURLSessionManager。AFHttpSessionManager继承自AFURLSessionManager,是主要http网络请求的接口暴露层。封装了get,post,put,delete,head,patch等标准http请求。在将具体讨论AF源码之前,先说下iOS7.0以后推出的应用层网络请求新类,NSURLSession && NSURLSessionTask及相关NSURLSessionTask子类和代理类。
NSURLSession 可以理解为一个连接池管理对象,而不是一个链接类。默认情况下,OS X 和iOS 中NSURLSession最大的链接数分别是6和4(详情见NSURLSessionConfiguration的HTTPMaximumConnectionsPerHost属性)。NSURLSession相比原来的NSURLConnection提供了一些新的好处是支持http2.0,而且提供了<font color=red> 链接复用 </font>,而且还提供了后台下载功能,也是就即使app退到后台,依然可以让下载任务不停。链接复用对于http/https请求性能提升是有很大好处的,不需要每次都建立链接的三次握手以及https的身份验证过程,具体性能提升原理和过程可以参考 IP,TCP 和 HTTP 和 别说你会AFNetworking3.0/NSURLSession 这两篇文章。顺便说一下直接使用原生的AFNetworking 3.0接口是不支持链接复用的,后见面解析源码的时候会发现AF每次发一个网络请求都会创建一个带默认配置的NSURLSession。
NSURLSessionTask是个抽象类,可以理解为任务就是一个请求。由于网络请求类型的不同,有的是发送一个get请求,拿到一小块数据,有的是上传一个比较大的文件,有的是要下载一个比较大的数据,由此而引申出来NSURLSessionTask的对应三个子类:NSURLSessionDataTask, NSURLSessionUploadTask, NSURLSessionDownloadTask和各自对应的相关代理类。大致知道这几个类的区别和使用场景就可以了,这些类具体详细区别了就不展开了。
AFHttpSessionManager 暴露接口
AFHttpSessionManger是开发者接触最多也是直接使用的类,它的功能是直接暴露出各种标准http方法共开发者调用。方法名字都是[url:parameters:successBlock:failedBlock]这种比较统一风格的,简单易用。它还有一些属性和构造方法:
/**
The URL used to construct requests from relative paths in methods like `requestWithMethod:URLString:parameters:`, and the `GET` / `POST` / et al. convenience methods.
*/
@property (readonly, nonatomic, strong, nullable) NSURL *baseURL;
/**
Requests created with `requestWithMethod:URLString:parameters:` & `multipartFormRequestWithMethod:URLString:parameters:constructingBodyWithBlock:` are constructed with a set of default headers using a parameter serialization specified by this property. By default, this is set to an instance of `AFHTTPRequestSerializer`, which serializes query string parameters for `GET`, `HEAD`, and `DELETE` requests, or otherwise URL-form-encodes HTTP message bodies.
@warning `requestSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPRequestSerializer <AFURLRequestSerialization> * requestSerializer;
/**
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) AFHTTPResponseSerializer <AFURLResponseSerialization> * responseSerializer;
///---------------------
/// @name Initialization
///---------------------
/**
Creates and returns an `AFHTTPSessionManager` object.
*/
+ (instancetype)manager;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
@param url The base URL for the HTTP client.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url;
/**
Initializes an `AFHTTPSessionManager` object with the specified base URL.
This is the designated initializer.
@param url The base URL for the HTTP client.
@param configuration The configuration used to create the managed session.
@return The newly-initialized HTTP client
*/
- (instancetype)initWithBaseURL:(nullable NSURL *)url
sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
baseURL类似host一样的东西,一旦设置好后,所有的网络请求都只写接口名字而不用写host,建议把这个baseURL作为一个宏或者配置文件,这样当整个app的后台服务器版本升级(一般会不会改域名,只是改version字段),只需要改一处就可以了。
manger方法是个类方法,看着名字很像单例是吧
+ (instancetype)manager {
return [[[self class] alloc] initWithBaseURL:nil];
}
为了可自己定制session,AFHttpSessionManger提供了initWithBaseURL:sessionConfiguration方法,但是这个方法里面又是这样滴:
- (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;
}
而在super方法里也就是AFURLSessionManager的initWithSessionConfiguration 方法里是每次都会新建一个session。也就是一个AFHttpSessionManager实例对应一个NSURLSession实例,虽然NSURLSession本身提供了链接复用,但是上层的“不当使用”关闭了它的功能。
其实也不是不当使用,在AFHttpSessionManager类的注释文件里,作者做了如下说明:
Developers targeting iOS 7 or Mac OS X 10.9 or later that deal extensively with a web service are encouraged to subclass AFHTTPSessionManager
, providing a class method that returns a shared singleton object on which authentication and other configuration can be shared across the application.
For developers targeting iOS 6 or Mac OS X 10.8 or earlier, AFHTTPRequestOperationManager
may be used to similar effect.
看到这句话其实作者希望我们自己去封装AFNetworking的,并使用单例的方式来调用。
AFHttpSessionManager的构造请求方法(上传文件除外,上传文件的body构造与一般的请求不一样,会用到输入流写到HTTPBodyStream中,而不是HTTPbody)最终都会调用
- (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
这个方法会构造好请求(具体的请求构造是在AFURLSessionManager 里完成的),生成一个NSURLSessionDataTask对象,并根据设置好相应的block回调。拿到这个task后接口方法调用task对象的resume的方法来发起网路请求。可以看到接口层比较简单,也应该比较简单。当然逻辑守恒定律,总有一些地方要复杂的。AFURLSessionManager 里面做了相对而言复杂的工作,包括构造各种请求(主要是各种填充头部和httpbody),然后实现各种不同task的代理方法来完成数据的组装等。
AFURLSessionManager 构造请求 && 处理数据回调
AFURLSessionManager 负责具体构造请求,并实现了NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate 这四个代理。用于处理收到网络响应时的回调处理。
先来看下AFURLSessionManager 都有哪些属性
@property (readonly, nonatomic, strong) NSURLSession *session;
/**
The operation queue on which delegate callbacks are run.
*/
@property (readonly, nonatomic, strong) NSOperationQueue *operationQueue;
/**
Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an instance of `AFJSONResponseSerializer`.
@warning `responseSerializer` must not be `nil`.
*/
@property (nonatomic, strong) id <AFURLResponseSerialization> responseSerializer;
///-------------------------------
/// @name Managing Security Policy
///-------------------------------
/**
The security policy used by created session to evaluate server trust for secure connections. `AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified.
*/
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;
#if !TARGET_OS_WATCH
///--------------------------------------
/// @name Monitoring Network Reachability
///--------------------------------------
/**
The network reachability manager. `AFURLSessionManager` uses the `sharedManager` by default.
*/
@property (readwrite, nonatomic, strong) AFNetworkReachabilityManager *reachabilityManager;
#endif
///----------------------------
/// @name Getting Session Tasks
///----------------------------
/**
The data, upload, and download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionTask *> *tasks;
/**
The data tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDataTask *> *dataTasks;
/**
The upload tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionUploadTask *> *uploadTasks;
/**
The download tasks currently run by the managed session.
*/
@property (readonly, nonatomic, strong) NSArray <NSURLSessionDownloadTask *> *downloadTasks;
///-------------------------------
/// @name Managing Callback Queues
///-------------------------------
/**
The dispatch queue for `completionBlock`. If `NULL` (default), the main queue is used.
*/
@property (nonatomic, strong, nullable) dispatch_queue_t completionQueue;
/**
The dispatch group for `completionBlock`. If `NULL` (default), a private dispatch group is used.
*/
@property (nonatomic, strong, nullable) dispatch_group_t completionGroup;
///---------------------------------
/// @name Working Around System Bugs
///---------------------------------
/**
Whether to attempt to retry creation of upload tasks for background sessions when initial call returns `nil`. `NO` by default.
@bug As of iOS 7.0, there is a bug where upload tasks created for background tasks are sometimes `nil`. As a workaround, if this property is `YES`, AFNetworking will follow Apple's recommendation to try creating the task again.
@see https://github.com/AFNetworking/AFNetworking/issues/1675
*/
@property (nonatomic, assign) BOOL attemptsToRecreateUploadTasksForBackgroundSessions;
///---------------------
/// @name Initialization
///---------------------
/**
Creates and returns a manager for a session created with the specified configuration. This is the designated initializer.
@param configuration The configuration used to create the managed session.
@return A manager for a newly-created session.
*/
- (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;
/**
Invalidates the managed session, optionally canceling pending tasks.
@param cancelPendingTasks Whether or not to cancel pending tasks.
*/
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;
session:创建的NSURLSession对象。由于NSURLSession本身的构造接口提供了一个回调队列,因此多了一个NSOperationQueue,如果不设置,NSURLSession会自动创建一个串行队列,所有的回调都将在这个队列里进行。
tasks:当前正在执行的全部task数组
dataTasks:当前正在执行datatask数组
uploadTasks:当前正在执行上传task数组
downloadTasks:当前正在执行的下载task数组
这些task在执行完了之后会被移除数组并销毁。
securityPolicy:在https链接当需要做身份验证时采取的安全策略。默认是不会进行证书验证。
接下来就是暴露给外面的构造请求的方法了,然后就是设置NSURLSession各种状态下的处理block。这个占据了太多篇幅,代理方法的名字就能很好表达方法的意思了,所以就不具体分析了。
需要注意的是处理数据的过程中,对于上传进度,下载进度是通过观察NSURLSessionTask的这几个属性
然后根据taskid取到task在执行相应的上传和下载进度回调。
接口层的分析基本就这些,下篇将具体分析在构造请求的过程中,是怎么构造的,对于文件上传的post请求又是怎么处理的以及对响应数据又是怎么处理的。