AFNetworking
AFNetworking 是 Objective-C 中用于网络请求的第三方框架,我们一般使用它来封装网络请求,这篇文章记录了阅读 AFNetworking(Version 3.1.0) 源码的笔记,简单的研究了它的实现细节。

前言

这里引用 @draveness 的一张图来展示 AFNetworking 的整个架构:

本系列以此架构为依据,随后的文章会逐一分析 AFNetworking 的实现原理。

流程

首先以官方 Demo 中的一个 Get 方法为例,来探究 AFNetworking 网络请求的大致流程。

  • 1.Get 请求入口
1
2
3
4
- (NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(id)parameters
success:(void (^)(NSURLSessionDataTask *task, id responseObject))success
failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure
  • 2.创建 NSURLSessionDataTask
1
2
3
4
5
6
7
- (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
  • 3.创建 NSURLSessionDataTask 前先创建 NSMutableURLRequest
1
2
3
4
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method
URLString:(NSString *)URLString

parameters:(id)parameters
error:(NSError *__autoreleasing *)error
  • 4.用 request 创建 NSURLSessionDataTask
1
2
3
4
- (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
  • 5.给 dataTask 添加 Delegate (主要为 task 提供进度管理功能)
1
2
3
4
- (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

以上方法是调用链上的主要方法,将在下文逐一解释。

1.Get 请求入口

具体方法体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 创建这个 NSURLSessionDataTask
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
/**
* session task的几种状态的操作函数
suspend -- 可以让当前的任务暂停
resume ---- 方法不仅可以启动任务,还可以唤醒suspend状态的任务
cancel ----- 方法可以取消当前的任务,你也可以向处于suspend状态的任务发送cancel消息,任务如果被取消便不能再恢复到之前的状态.
*/
[dataTask resume];

使用 GET 类型的 Request 来创建并运行一个 NSURLSessionDataTask,这里要注意的是用[dataTask resume]来开启这个 task。

2.创建 NSURLSessionDataTask

主要方法体如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
...
// 创建 NSMutableURLRequest
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
// 处理构建 request 产生的错误
...
// 创建 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;

dataTaskWithHTTPMethod:这个函数的实现主要分两部分,一部分是构建NSMutableURLRequest,另一部分是根据已构建好的 Request 来创建 DataTask。
其中 Request 是由传递的 method 来创建,这里传递的是 GET,其它参数还有HEAD、POST、PUT、PATCH、DELETE 。

3.创建 NSMutableURLRequest

使用指定的 HTTP method 和 URLString 来构建一个 NSMutableURLRequest 对象实例,主要方法体如下:

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
 	// 参数断言
NSParameterAssert(method);
NSParameterAssert(URLString);

NSURL *url = [NSURL URLWithString:URLString];

NSParameterAssert(url);

// 使用url构建并初始化NSMutableURLRequest,然后设置HTTPMethod
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;

// 给NSMutableURLRequest自带的属性赋值
// 然后通过判断mutableObservedChangedKeyPaths(NSMutableSet)中是否有这个keyPath,来设定mutableRequest对应的keyPath值
// AFHTTPRequestSerializerObservedKeyPaths这个数组里的属性是固定的,且在 init 方法里全都 KVO
/**
* 当 AFHTTPRequestSerializerObserverContext 中有 value 变化了(且变化后的新值不为 NSNull null),就会响应 observerValueForKeyPath 这个函数,从而mutableObservedChangedKeyPaths就会添加这个 keyPath
*/
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}

// 将传入的parameters进行编码,并添加到request中
/**
* 一般我们请求都会按key=value的方式带上各种参数,GET方法参数直接加在URL上,POST方法放在body上,NSURLRequest没有封装好这个参数的解析,只能我们自己拼好字符串。AFNetworking提供了接口,让参数可以是NSDictionary, NSArray, NSSet这些类型,再由内部解析成字符串后赋给NSURLRequest
*/
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];

return mutableRequest;

如果 method 是 GET、HEAD、DELETE,那 parameter 将会被用来构建一个基于 url 编码的查询字符串(query url),并且这个字符串会直接加到request的url后面。对于 POST/PUT,它们会根据 parameterEncoding 属性进行编码,而后加到 request 的 http body 上。

4.用 request 创建 NSURLSessionDataTask

主要方法体如下:

1
2
3
4
5
6
7
__block NSURLSessionDataTask *dataTask = nil;
// 解决iOS8之前的一个bug
url_session_manager_create_task_safely(^{
// 用NSURLSession创建NSURLSessionDataTask
dataTask = [self.session dataTaskWithRequest:request];
});
...

用 NSURLSession 的 dataTaskWithRequest:方法来创建 dataTask,这里需要注意的是方法被一个url_session_manager_create_task_safely的 block 包起来,这里是为了解决 iOS 8 上的一个 bug

5.给 dataTask 添加 Delegate

在这个方法里给 dataTask 添加了一个 AFURLSessionManagerTaskDelegate,并为 dataTask 提供进度管理功能,具体的是在[self setDelegate:delegate forTask:dataTask]方法:

1
2
3
4
5
6
7
8
9
10
NSParameterAssert(task);
NSParameterAssert(delegate);

[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 设置uploadProgress和downloadProgress这两个NSProgress变量
[delegate setupProgressForTask:task];
// 给session task添加KVO
[self addNotificationObserverForTask:task];
[self.lock unlock];

这里首先把task.taskIdentifier作为 key,delegate 作为 value 存在一个 MutableDictionary 里,然后设置上传和下载两个 progress,并给 task 添加上启动和暂停的 KVO。

小结

通过这个流程可以初步了解 AFNetworking 内部方法调用关系,当然也可以看出其实 AFNetworking 就是对 NSURLSession 的高度地封装。随后的文章会结合源码,来深入理解 AFNetworking 的实现原理。