iOS开发——AFNetworking源码(一)
简介
我们在学习AFNetworking之前,就必须知道AFNetworking是什么,具体有什么作用?
首先,AFNetworking是一个功能非常强大的框架,主要是用于网络请求,把复杂的原生代码封装好,我们只需要简单的几步就可以完成网络请求。
代码组成
从上图可以看出AFNetworking中含有很多类,那么这些类与类之间有什么关系呢,具体关系如图所示:
我们来解读一下这个图,按照我的理解来说,显而易见的是AFURLSessionManager应该是主力军,因为左边黄色框框里的四个类都是服务于AFURLSessionManager,举个例子,AFURLSessionManager如果是前方部队的话,其余四个就是他的后备军,所以只有AFURLSessionManager管理好这四个类,它才可以好好工作。(也就是说生成对应的sessionmanger,并且初始化并管理AFSecurityPolicy、AFNetworkReachabilityManager以及AFJSONResponseSerializer来一同为它所生成的sessionmanger服务)。
而AFHTTPSessionManager是继承于AFURLSessionManger的子类,内部管理自己的AFHTTPResponseSerializer和AFHTTPRequestSerializer两种序列化工具,用来对请求和响应的数据来做序列化;同时依赖于父类提供的保证安全,监控网络状态,实现发出HTTP请求的核心功能;AFHTTPSessionManager本身是对网络请求做了一些简单的封装,请求的整个逻辑是分发给AFURLSessionManager或者其他类去做的;
模块划分
AFNetworking可以分成五个部分,具体如下:
- 网络通信模块(AFURLSessionManager、AFHTTPSessionManager)
- 网络状态监听模块(AFNetworkReachabilityManager)
- 网络通信安全策略模块(AFSecurityPolicy)
- 网络通信信息序列化模块(AFURLRequestSerialization,AFURLResponseSerialization)
- iOS UIkit库的拓展(UIKit)
我们可以看到AFNetWorking大致分为一下五大模块,其中最为核心的是网络通信模块,主要用来发送和响应请求;AFNetworkReachabilityManager主要是用来进行网络状态的监听,在不同网络状态下请求和响应的不同操作和处理;为了保证网络请求的安全性,当然少不了AFSecurityPolicy,网络安全策略模块,在初始化sessionmanger的时候同时初始化了AFSecurityPolicy以及网络通信信息请求序列化的类。网络通信信息序列化模块分为请求序列化和响应序列化,其内部分别会对请求头和响应头进行编码,在请求的时候,将请求头转码为计算机可识别的格式,在响应的时候将响应结果进行转码后传回给客户端。这几个模块息息相关,都是为一次HTTP请求的正常请求和响应做保障。最后一个模块就是基于UIKit库的一些相关拓展,其中包括UIImageView的请求,UIButton的请求等等。
AFNetworking使用流程
我们在使用AFNetworking进行网络请求的时候,首先需要生成对应的sessionManager,以我项目中的代码为例:
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
我们要是直接看源码去学习关于上面的代码干了什么会有一些困难,所以我们可以先看一下下面的图解:
我们通过调用AFHTTPSessionManager的manager方法来进行初始化,那么在源码中manager方法实现了什么呢?
我们跳转到源码可以发现
由此可见,在AFHTTPSessionManager中的初始化方法最终都会调用其父类的initWitchSessionConfiguration初始化方法,返回一个sessionManager方法;那么,需要去看一下父类也就是AFURLSessionManager的初始化都做了什么:
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
self = [super init];
if (!self) {
return nil;
}
//如果会话配置为nil的话,对应初始化一个
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;
//初始化操作队列,并设置为串行队列 设置最大并发操作数
//1.⚠️
self.operationQueue = [[NSOperationQueue alloc] init];
self.operationQueue.maxConcurrentOperationCount = 1;
//AFJSONResponseSerializer用来序列化HTTP响应
self.responseSerializer = [AFJSONResponseSerializer serializer];
//初始化SSI所需要的AFSecurityPolicy用来保证请求的安全性
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
#if !TARGET_OS_WATCH
//AFNetworkReachabilityManager用来查看网络连接状态
self.reachabilityManager = [AFNetworkReachabilityManager sharedManager];
#endif
//初始化可变任务字典task的id作为key,代理对象作为value
//2.⚠️
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];
//初始化锁和锁的命名
self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;
//获取所有的task,设置一遍delegate用来异步的获取当前session的所有未完成的task,session和task的关系是一个session里有多个task
//初始化dataTasks一般为空,这里这么做的主要目的是为了防止从后台回来,重新初始化整个session,一些之前的后台请求任务,导致程序崩溃
//3.⚠️
[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;
}
总结以下几点:
- 初始化当前的会话配置,操作队列,锁,AFNetworkReachabilityManager,AFSecurityPolicy,请求序列化以及用来存储任务的可变任务字典等属性;
- 获取当前session中所有未完成的task,给它们设置一遍代理;
这个函数有三个需要注意的点:
队列的最大并发操作数设置为1,这里的并发操作数值的是回调代理的线程并发数。
- self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];是用来将每一个请求任务和自定义的AFURLSessionManagerTaskDelegate来建立映射的;(需要深入研究,代理和这里的关系,以及利用KVO的思想实现的相关)
- 在初始化的时候获取当前session中的所有task,为它们重新设置一遍代理;一般来说初始化的session中的task应该是为空的,这里这么做的主要目的是为了防止从后台回来的时候初始化session,对于一些后台之前的请求任务没有重设代理导致崩溃的问题;这里里边不同的任务调用不同的addDelegateForXXX方法来设置代理,看一下这些方法的实现:
//为data任务添加代理
- (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,弱持有AFURLSessionManager,设置其manager以及completionHandler;
//由于这里弱持有AFURLSessionManager,所以不会造成循环引用的问题;
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:dataTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
//设置会话任务的任务描述
dataTask.taskDescription = self.taskDescriptionForSessionTasks;
//调用setDelegate存储dataTask
[self setDelegate:delegate forTask:dataTask];
delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
//为upload任务添加代理
- (void)addDelegateForUploadTask:(NSURLSessionUploadTask *)uploadTask
progress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:uploadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
uploadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:uploadTask];
delegate.uploadProgressBlock = uploadProgressBlock;
}
//为任务download添加代理
- (void)addDelegateForDownloadTask:(NSURLSessionDownloadTask *)downloadTask
progress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
destination:(NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
completionHandler:(void (^)(NSURLResponse *response, NSURL *filePath, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate alloc] initWithTask:downloadTask];
delegate.manager = self;
delegate.completionHandler = completionHandler;
if (destination) {
delegate.downloadTaskDidFinishDownloading = ^NSURL * (NSURLSession * __unused session, NSURLSessionDownloadTask *task, NSURL *location) {
return destination(location, task.response);
};
}
downloadTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:downloadTask];
delegate.downloadProgressBlock = downloadProgressBlock;
}
这三个方法都是为不同的任务设置代理,最终都会调用setDelegate设置代理并存储datatask任务;
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
这个方法主要是把代理和task建立映射关系,并且存储到事先声明好的字典当中;同时为当前task添加监听;在添加监听的方法中,taskDidResume和taskDidSuspend为接收到通知后的处理方法;用来恢复任务和暂停任务;至此,对于初始化的时候获取当前session中的所有task,已经为它们重新设置一遍代理。回到initWitchSessionConfiguration方法中返回当前对象,向上返回,生成AFHTTPSessionManager *sessionManger对象;
一般我们运用网络请求的方法主要有GET和POST两种,以GET为例以下是图解流程,然后对比源码层面进行分析
GET源码内部做了以下工作:
/*URLString URL值
parameters 根据客户端请求序列化程序编码的参数
headers 附加到此请求头的头部标识
uploadProgress 更新要上传进度时要执行的块对象
downloadProgress 更新要下载进度时要执行的块对象。注意这个块是在会话队列上调用的,而不是在主队列上
success 当任务成功完成时要执行的块对象。该块没有返回值,并接受两个参数:data参数和客户端响应序列化创建的响应对象
failure 当任务未成功完成或成功完成时执行的块对象,但在解析响应数据时遇到错误。该块没有返回值,并接受两个参数:数据任务和描述发生的网络或解析错误的错误
*/
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
progress:(nullable void (^)(NSProgress * _Nonnull))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask * _Nonnull, id _Nullable))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable, NSError * _Nonnull))failure
{
//调用dataTaskWithHTTPMethod方法生成一个datatask任务
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
headers:headers
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}
GET方法内部一是生成datatask,二是开启请求;
生成datatask进入到AFHTTPSessionManager中的dataTaskWithHTTPMethod方法,进去看看:
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(nullable id)parameters
headers:(nullable NSDictionary <NSString *, NSString *> *)headers
uploadProgress:(nullable void (^)(NSProgress *uploadProgress)) uploadProgress
downloadProgress:(nullable void (^)(NSProgress *downloadProgress)) downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure
{
//序列化错误
NSError *serializationError = nil;
//设置request相关属性&参数。(requestSerializer:请求序列化器)
//1.⚠️使用请求序列化类中的requestWithMethod方法进行序列化处理。
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
//设置请求头
for (NSString *headerField in headers.keyEnumerator) {
[request setValue:headers[headerField] forHTTPHeaderField:headerField];
}
//序列化失败的回调处理
if (serializationError) {
if (failure) {
//completionQueue如果设置了这个GCD的queue,那么从这个completionQueue回调就好;否则就从主队列回调
dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{
failure(nil, serializationError);
});
}
return nil;
}
//NSURLSessionManger创建一个dataTask
//2.⚠️调用dataTaskWithRequest来生成一个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;
}
解决一下两个问题:
1.调用请求序列化类中的requestWithMethod方法进行序列化处理
通过requestSerializer来调用requestWithMethod对请求参数进行序列化,最终生成一个最终请求网络需要的request实例;那么,为什么要进行序列化?那这里,先看看requestWithMethod方法内部都做了些什么:
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
//判断参数是否存在url转化
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
//利用url创建NSMutableURLRequest并设置http请求方法
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
//循环遍历mutableRequest
//在self.mutableObservedChangedKeyPaths根据keyPath取出对应的value值,存到创建的mutableRequest
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
//对mutableRequest参数做编码并且重新赋值给mutableRequest
//⚠️调用requestBySerializingRequest方法将传入的parameters参数进行编码,并添加到request
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
总结一下:
在requestWitchMethod方法中,做了三件事情:
- 创建mutableRequest并设置其请求方法;
- 把当前类设置的一些属性设置给mutableRequest;(存在疑问)
- 把需要传递的参数进行编码并且设置到mutableRequest当中;
看看编码的方法:
- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
NSParameterAssert(request);
NSMutableURLRequest *mutableRequest = [request mutableCopy];
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
if (self.queryStringSerialization) {
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
if (serializationError) {
if (error) {
*error = serializationError;
}
return nil;
}
} else {
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {
// #2864: an empty string is a valid x-www-form-urlencoded payload
if (!query) {
query = @"";
}
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}
- 从当前请求队列中拿到self.HTTPRequestHeaders中拿到设置的参数,赋值要请求的request中;(疑问点)
- 把网络请求的参数转换为NSString类型,这一步是对参数进行转码;
- 将请求方法拼接到url中;GET、HEAD、DELETE这几个method的quey是拼接到url后面的。而POST、PUT是把query拼接到http body中的;
以下这几个方法就是我们在转码的时候所使用的方法,如何将请求参数进行转码:也就是通过递归调用并解析,直到解析的除了array,dic,set以外的元素,然后将最终得到的参数返回;
到这里序列化的 部分结束,返回一个NSMutableURLRequest实例;紧接着设置请求头并且通过回调处理序列化失败的情况;
那么了解以后,我们回答一下为什么要进行序列化?
在HTTp网络请求是基于字节流的网络传输,序列化是可以将一个对象转化成一段字节编码,以此方便在网络上传输或者做其他存储处理,使用的时候在对其做反序列化;简单的来说就是为了统一,我们可以用自己的方法来保存对象,但是在底层只提供一种保存对象状态的机制。因此我们在存储的时候需要进行序列化,以此来和提供的保持一致,在读取的时候对其进行反序列化,得到我们想要的形式;
2.调用dataTaskWithRequest来生成一个datatask任务
在调用的时候,将序列化后的request,等参数传入,先看看dataTaskWithRequest方法里边做了什么事情吧:
- (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 {
NSURLSessionDataTask *dataTask = [self.session dataTaskWithRequest:request];
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}
可以看到,生成一个datatask,并且调用addDelegateForXXX方法来设置代理,那么,设置代理这一块,和在初始化的时候为当前初始化session中的task设置代理的过程是一样的。(向上👆查看初始设置代理部分);
最终返回一个dataTask任务,创建完成之后通过回调返回进度以及comple情况,在dataTaskWithHTTPMethod方法中进行处理;这里回到dataTaskWithHTTPMethod方法中,做完上述处理之后,最终将生成的dataTask返回到GET方法当中,这样最终在GET中就拿到了我们可以发送请求的task,最后调用系统方法[dataTask resume**];发起网络请求;当然,在GET方法中有progress,success,failure回调,使用的时候直接根据回调来处理不同情况,不用像原生的方法那样,进行多次判断,确认哪一种情况,再去做处理;
回到我们的初始化方法,在调用AFURLSessionManager的initWithSessionConfiguration方法初始化一个sessionManager的时候,在进行懒加载的时候,初始化session: