AFNetWorking 源码学习笔记 ☞ 主体流程
一、简介
AFNetworking 是使用 Objective-C 开发 iOS App 时首选的第三方网络框架,当然早期还有 ASI 的框架,不过因为长期无人维护,已很少与人使用了。本系列主要是对 AFNetWorking (3.2.1) 源码学习的一个记录,共分 5 篇进行讨论,目录如下:
AFNetWorking 源码学习笔记 ☞ 主体流程
AFNetWorking 源码学习笔记 ☞ NSURLSession
AFNetWorking 源码学习笔记 ☞ Security
AFNetWorking 源码学习笔记 ☞ Serialization
AFNetWorking 源码学习笔记 ☞ Reachability
本文是第一篇,简要介绍一下框架结构和主体流程。
二、框架结构
为了找到一个合适的切入点,首先查看了 github 上的文档,然后根据示例写了一个粗略的 demo,使用 cocoapods 导入了 AFNetWorking 之后,查看其文件结构,如下图所示:
AFNetWorking-文件结构.png可以看到,除 UIKit 之外,主要分了 4 部分,各部分的主要作用如下:
-
NSURLSession,包括 2 个主要的类,AFHTTPSessionManager 和 AFURLSessionManager,前者提供一些对外接口,它继承自 AFURLSessionManager 这个核心类,主要的工作都在这里,包括创建及启动 task,代理方法的处理等等。
-
Security,只有一个类 AFSecurityPolicy,代码也不多,负责在系统底层认证 HTTPS 之前,AFNetWorking 自己做一次认证。
-
Serialization,包含 2 个类,AFURLRequestSerialization 和 AFURLResponseSerialization 分别负责请求参数和接口返回结果的处理。
-
Reachability,提供了一个监听网络状态的类 AFNetworkReachabilityManager,不太常用,我们一般都是用苹果提供的 Reachability 这个类,然后自己封装一下,不过需要手动将它的 .h/.m 文件导入到工程里。
三、主体流程
本文的 demo 创建了一个简单的网络请求,我们以此作为突破口,开始一步一步研究这个框架的基本流程。下边是自己添加的发送请求的代码。
#import "ViewController.h"
#import <AFNetworking/AFNetworking.h>
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self startLoadData];
}
- (void)startLoadData {
// 1.拼接参数
NSString *urlString = [[unsplash_ENDPOINT_HOST stringByAppendingString:unsplash_ENDPOINT_POPULAR] stringByAppendingString:unsplash_CONSUMER_KEY_PARAM];
NSUInteger nextPage = 1;
NSString *imageSizeParam = @"&image_size=600";
NSString *urlAdditions = [NSString stringWithFormat:@"&page=%lu&per_page=%d%@", (unsigned long)nextPage, 10, imageSizeParam];
NSString *URLString = [urlString stringByAppendingString:urlAdditions];
// 2.发送请求
// *** 点开此处 GET 方法
[[AFHTTPSessionManager manager] GET:URLString parameters:urlAdditions progress:^(NSProgress * _Nonnull downloadProgress) {
NSLog(@"+++++> 进行中...:%@", downloadProgress);
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(@"=====> 成功:%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (error) {
NSLog(@"-----> 失败原因:%@", error.domain);
}
}];
}
在 GET 方法上 “command + 右键” 跳转到位于 AFHTTPSessionManager.m 文件中的方法实现(见下方代码),做了两件事:
①创建 dataTask;
②启动任务(使用 resume 可能有感觉有点怪,不过苹果文档给出的解释是,新创建的 task 是处于挂起状态的,需要使用 resume 启动任务)。
- (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: 方法,看看他里边究竟做了什么。我们发现其实也差不多做了两件事:
①使用 requestSerializer 这个类创建 NSMutableURLRequest 对象;
②又是创建 task ,这里将 上一步得到的 request 及下载、上传的 block 作为参数传入下一层创建 task 的方法,该方法位于 AFURLSessionManager 类中,它是 AFHTTPSessionManager 的父类,主要工作都在这个类里边处理。
- (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.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;
}
// 1.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;
}
发现这里创建 request 和 创建 task 的方法还可继续深入,先点开 requestWithMethod 方法看看,从下边的代码中可以发现,这里主要就是调用系统方法创建 NSMutableURLRequest,然后设置参数并序列化,最后将该 request 返回。
这里说件出糗的事,记得刚学编程的时候曾被人问过 URLRequest 是否可以发起网络请求,还信心满满的回答可以,心想 request 翻译过来可不就是请求吗 😓。简单点说,request 在这里的作用其实就是拼装参数。
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString
parameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 0.检测 method、URLString 是否为空
NSParameterAssert(method);
NSParameterAssert(URLString);
NSURL *url = [NSURL URLWithString:URLString];
NSParameterAssert(url);
// 1.开始创建 NSMutableURLRequest,因为 NSURLRequest 的属性都是 readonly,为了能够修改,只能创建可变的了。
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
// 2.设置参数
mutableRequest.HTTPMethod = method;
// 为 request 设置其一些默认参数
// AFHTTPRequestSerializerObservedKeyPaths():AFHTTPRequestSerializer 的 6个属性对应 get特然 的 方法名string 组成的数组
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
// self.mutableObservedChangedKeyPaths 中是 值非nil 的属性名
// requstSerializer 初始化时,创建了 self.mutableObservedChangedKeyPaths 这个空 mutableSet,然后,如果设置了对应的参数,它里边就会加上对应的属性名
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
// 3.对参数进行序列化,并赋值给 request,另外,设置必要的 header。
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}
再来看看这一层创建 task 的方法:
// 供子类调用
- (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 件事:
①先是 利用 session 创建 task(这是系统方法)。
这里用了 self.session,于是我们回到 AFURLSessionManager 的初始化方法里,查看 session 的创建过程,非常简单,只有一句:
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];
这里我只关注 delegate,这说明网络请求过程中的代理方法,将会在 AFURLSessionManager 这个类里边实现。
②然后 为 task 添加代理。
添加代理的方法是将 delegate(AFURLSessionManagerTaskDelegate)和 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;
}
请求开始前的准备工作基本已经做完了,现在让我们看看请求发起后都做了什么,当然是看 AFURLSessionManager 里边的实现的代理方法了,因为代理方法较多,这里挑其中最重要的 2 个作简要介绍。
1.如何接受挑战
- (void)URLSession:(NSURLSession *)session
didReceiveChallenge:(NSURLAuthenticationChallenge *)challenge
completionHandler:(void (^)(NSURLSessionAuthChallengeDisposition disposition, NSURLCredential *credential))completionHandler
{
NSURLSessionAuthChallengeDisposition disposition = NSURLSessionAuthChallengePerformDefaultHandling;
__block NSURLCredential *credential = nil;
if (self.sessionDidReceiveAuthenticationChallenge) {
disposition = self.sessionDidReceiveAuthenticationChallenge(session, challenge, &credential);
} else {
if ([challenge.protectionSpace.authenticationMethod isEqualToString:NSURLAuthenticationMethodServerTrust]) {
if ([self.securityPolicy evaluateServerTrust:challenge.protectionSpace.serverTrust forDomain:challenge.protectionSpace.host]) {
credential = [NSURLCredential credentialForTrust:challenge.protectionSpace.serverTrust];
if (credential) {
disposition = NSURLSessionAuthChallengeUseCredential;
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
} else {
disposition = NSURLSessionAuthChallengeCancelAuthenticationChallenge;
}
} else {
disposition = NSURLSessionAuthChallengePerformDefaultHandling;
}
}
if (completionHandler) {
completionHandler(disposition, credential);
}
}
2.请求结束成功或失败的回调
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
// delegate may be nil when completing a task in the background
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];
}
if (self.taskDidComplete) {
self.taskDidComplete(session, task, error);
}
}
转发给了代理
#pragma mark - NSURLSessionTaskDelegate
- (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;
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];
});
});
});
}
}
四、小结
至此,我们大概了解了一下 AFNetWorking 工作的主体流程,简单画了个图总结一下,当然略去了很多细节,不过都将会在后边的章节继续讨论。
AFNetWorking 主体流程.png