AFNetWorking 源码学习笔记 ☞ 主体流程

AFNetWorking 源码学习笔记.png

简介

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 部分,各部分的主要作用如下:

  1. NSURLSession,包括 2 个主要的类,AFHTTPSessionManager 和 AFURLSessionManager,前者提供一些对外接口,它继承自 AFURLSessionManager 这个核心类,主要的工作都在这里,包括创建及启动 task,代理方法的处理等等。

  2. Security,只有一个类 AFSecurityPolicy,代码也不多,负责在系统底层认证 HTTPS 之前,AFNetWorking 自己做一次认证。

  3. Serialization,包含 2 个类,AFURLRequestSerialization 和 AFURLResponseSerialization 分别负责请求参数和接口返回结果的处理。

  4. Reachability,提供了一个监听网络状态的类 AFNetworkReachabilityManager,不太常用,我们一般都是用苹果提供的 Reachability 这个类,然后自己封装一下,不过需要手动将它的 .h/.m 文件导入到工程里。

主体流程

本文的 demo 创建了一个简单的网络请求,我们以此作为突破口,开始一步一步研究这个框架的基本流程。下边是自己添加的发送请求的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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 启动任务)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (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 的父类,主要工作都在这个类里边处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
- (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 在这里的作用其实就是拼装参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
- (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 的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 供子类调用
- (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 的创建过程,非常简单,只有一句:

1
self.session = [NSURLSession sessionWithConfiguration:self.sessionConfiguration delegate:self delegateQueue:self.operationQueue];

这里我只关注 delegate,这说明网络请求过程中的代理方法,将会在 AFURLSessionManager 这个类里边实现。

②然后 为 task 添加代理。
添加代理的方法是将 delegate(AFURLSessionManagerTaskDelegate)和 task 的标识组成的键值对存储在当前对象中的一个字典里边,以备使用,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (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.如何接受挑战

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
- (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.请求结束成功或失败的回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (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);
}
}

转发给了代理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
#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

Demo及源码注释

HHAFNetworkingStudy

0%