如人饮水,冷暖自知

iOS 框架详解—「AFNetworking 框架结构及逻辑处理过程」

引导


AFNetWorking 基本是 iOS 开发中使用网络通信框架的标配,这个框架本身比较庞大,也很复杂,但是使用起来非常非常简单。极大地提高了开发效率,让我们更加专注于业务逻辑的实现。

本篇文章主要从【AFNetworking3.1 框架结构 & 内部逻辑处理过程】学习总结,该模块学习将续更 ~
在「时间 & 知识 」有限内,总结的文章难免有「未全、不足 」的地方,还望各位好友指出,可留言指正或是补充,以提高文章质量@白开水ln原著;

目录:

  1. AFN 需求 | 版本区别
  2. AFN 内部体系结构
  3. AFN 工程目录
  4. AFN GET内部逻辑处理
  5. AFN POST内部逻辑处理
  6. 总结
  7. SourceCodeToolsClassWechatPublic-Codeidea

首先来看一下,官方介绍 AFNetworking,如下:

AFNetworking is a delightful networking library for iOS and Mac OS X. It’s built on top of the Foundation URL Loading System, extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.

Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac.

Choose AFNetworking for your next project, or migrate over your existing projects—you’ll be happy you did!

简单释义:
AFNetworking 是一个适用于 iOS 和 Mac OS X 两个平台的网络库,它是基于 Foundation URL Loading System 上进行了一套封装,并且提供了丰富且优美的API接口给使用者使用。

Requirements【需求 | 版本区别】


AFN --> 需求 | 版本区别

  • 1.x 版本,内部底层是基于 NSURLConnection 的,是对 NSURLConnection 一次封装。

  • 在13年,苹果推出 NSURLSession 也就是会话管理者,后来 2.x AFN框架又对 NSURLSession 进行一次封装,其实在 2.0-2.6.3 AFN内部是使用两套系统,一套是基于 NSURLConnection的,一套是基于 NSURLSession的。

  • 版本升级到3.0之后,AFN 就不在支持 NSURLConnection 了,把有关 URLConnection 的代码已全部移除。

Architecture【AFN 内部体系结构】

  • NSURLSession【管理者】

    • AFURLSessionManager
    • AFHTTPSessionManager(封装了常用的 HTTP 方法)
      • GET
      • POST
      • UIKit + AFNetworking 分类
      • NSProgress:利用KVO
  • Serialization【序列化】

    • AFURLRequestSerialization【请求序列化】
      • AFHTTPRequestSerializer(请求的数据格式,默认二进制)
      • AFJSONRequestSerializer
      • AFPropertyListRequestSerializer
    • AFURLResponseSerialization【响应者序列化】
      • AFHTTPResponseSerializer(默认二进制响应数据,解析方案)
      • AFJSONResponseSerializer(返回JSON类型,JSON解析方案.默认)
      • AFXMLParserResponseSerializer(返回XML类型,XML解析方案)
  • Additional Functionality【额外的功能】

    • AFSecurityPolicy【安全策略相关】
    • HTTPS(HTTP+SSL加密协议)
    • AFNetworkReachabilityManager【网络状态监听管理者】
  • UIKit【类库扩展与工具类】

    • UIKit+AFNetworking
  • NSURLConnection(iOS9.0废弃)

    • AFURLConnectionOperation
    • AFHTTPRequestOperation
    • AFHTTPRequestOperationManager(封装了常用的 HTTP 方法)
    • 属性
      • baseURL:AFN建议开发者针对 AFHTTPRequestOperationManager 自定义个一个单例子类,设置 baseURL, 所有的网络访问,都只使用相对路径即可
      • requestSerializer:请求数据格式/默认是二进制的 HTTP
      • responseSerializer:响应的数据格式/默认是 JSON 格式
      • operationQueue:操作队列
      • reachabilityManager:网络状态检测管理者
    • 方法
      • manager :方便创建管理器的类方法
      • HTTPRequestOperationWithRequest :在访问服务器时,如果 要告诉服务器一些附加信息,都需要在 Request 中设置
      • GET
      • POST

Directory【AFN 工程目录】


目前版本是 3.1.0,我通过 CocoaPods 导入的 AFNetworking,导入后目录如下

CocoaPods-->AFN 3.1.0工程目录

使用 CocoaPods 导入后可以看到目录很清晰主要是在五个文件夹下, 除去 Support Files,可以看到AF分为如下5个功能模块:

  • NSURLSession(网络通信模块)

  • ReachAbility(网络状态监听模块)

  • Security(网络通信安全策略模块)

  • Serialization(网络通信信息序列化/反序列化模块)

  • UIKit(UIKit库的扩展)

其核心当然是网络通信模块,其余的四个模块,均是为了配合网络通信或对已有 UIKit 的一个扩展及工具包。
这五个模块所对应的类的结构关系图如下所示:
AFN 功能模块-->关系

可以看到,AFN 的核心是 AFURLSessionManager 类,AFHTTPSessionManager 继承于 AFURLSessionManager, 针对HTTP协议传输做了特化。而 AFURLResponseSerializationAFSecurityPolicyAFNetworkReachabilityManager则被AFURLSessionManager所用。
其次,还可以看到一个单独的UIKit 包提供了对 iOS UIKit 类库的扩展与工具类。

建议:
可以学习下AFN对 UIKit 做了一些分类,对自己能力提升是非常有帮助的。

当下载好,手动导入的时候,显示两个文件夹,如下
手动导入-->AFN 3.1.0工程目录

很明显第一个文件夹里边是跟网络请求相关的,第二个是跟UI相关的。

补充




Business logic【AFN GET 内部逻辑处理】


这是 AFNetworking 发起一个 Get 请求的流程图,大概可以分为这几个步骤,下面会逐个解读这个流程。
AFN-->GET业务逻辑处理.png

1. AFHTTPSessionManager 发起GET请求

manager-->GET请求

这个方法是 AFN 的 Get请求 的起点,其他 Get 请求的方法也都是直接或者间接调用这个方法来发起 Get 请求。这个方法的代码量很少也很直观,就是调用其他方法生成 NSURLSessionDataTask对象的实例,然后调用 NSURLSessionDataTaskresume 方法发起请求。

2. 创建 NSURLSessionDataTask

创建-->NSURLSessionDataTask

这个方法是创建 NSURLSessionDataTask 对象实例并返回这个实例。首先创建一个 NSMutableURLRequest 对象的实例,然后配置。之后是使用 NSMutableURLRequest 对象的实例创建NSURLSessionDataTask 对象实例,然后配置,可以选择性地传入各类Block回调,用于监听网络请求的进度比如上传进度,下载进度,请求成功,请求失败。

3. 配置 NSMutableURLRequest对象

配置-->NSMutableURLRequest对象

在这个方法中先使用了 url 创建了一个 NSMutableURLRequest 对象的实例,并且设置了 HTTPMethodGet 方法(如果是Post方法,那么这里就是设置Post方法)然后使用KVC的方法设置了 NSMutableURLRequest 的一些属性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 设置 NSMutableURLRequest 的属性
static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
//allowsCellularAccess 允许使用数据流量
//cachePolicy 缓存策略
//HTTPShouldHandleCookies 处理Cookie
//HTTPShouldUsePipelining 批量请求
//networkServiceType 网络状态
//timeoutInterval 超时
_AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
});
return _AFHTTPRequestSerializerObservedKeyPaths;
}

配置-->NSMutableURLRequest对象
先设置 HTTP header,之后格式化请求参数,设置参数的编码类型。这个是这个方法的基本操作流程。对于Get操作来说,参数是直接拼接在请求地址后面。

4. 配置 NSURLSessionDataTask对象

配置-->NSURLSessionDataTask对象

之后配置 NSMutableURLRequest 对象就需要配置 NSURLSessionDataTask 对象了。主要分为2个步骤,第一个步骤是创建 NSURLSessionDataTask 对象实例,第二个步骤是给NSURLSessionDataTask 对象实例设置 Delegate。用于实时了解网络请求的过程。

给NSURLSessionDataTask对象实例设置Delegate.png

AFN 的代理统一使用 AFURLSessionManagerTaskDelegate 对象来管理,使用 AFURLSessionManagerTaskDelegate 对象来接管NSURLSessionTask 网络请求过程中的回调,然后再传入 AFN 内部进行管理。

1
2
@interface AFURLSessionManagerTaskDelegate : NSObject <NSURLSessionTaskDelegate,
NSURLSessionDataDelegate, NSURLSessionDownloadDelegate>

如代码所示 AFURLSessionManagerTaskDelegate 接管了NSURLSessionTaskDelegateNSURLSessionDataDelegateNSURLSessionDownloadDelegate 的各种回调,然后做内部处理。这也是第三方网络请求框架的重点,让网络请求更加易用,好用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 通过 task 的标识符管理代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
// 将task和代理类绑定,task的taskIdentifier作为字典的key,delegate作为字典的value
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
// 给该task添加两个KVO事件(Resume 和 Suspend)
[self addNotificationObserverForTask:task];
[self.lock unlock];
}

通过NSURLSessionTasktaskIdentifier标识符对delegate进行管理,只要是用于识别该NSURLSessionTask的代理。


NSURLSessionTask 设置进度回调
设置各类回调 Block,给 NSURLSessionTask 使用 KVO 进行各种过程进度监听。

1
2
3
4
5
6
#pragma mark -
// 给task添加暂停和恢复的通知
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task {
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidResume:) name:AFNSURLSessionTaskDidResumeNotification object:task];
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(taskDidSuspend:) name:AFNSURLSessionTaskDidSuspendNotification object:task];
}

监听 NSURLSessionTask 被挂起 和 恢复的通知。


5. 网络请求开始

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
// 发送GET请求
/**
GET: 请求路径(不包含参数),url
parameters: 字典(发送给服务器的数据~参数)
progress: 进度回调
success: 成功回调(task:请求任务、responseObject:响应体信息JSON->OC对象)
failure: 失败回调(error:错误信息)
task.response: 响应头
*/
- (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
{
NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET"
URLString:URLString
parameters:parameters
uploadProgress:nil
downloadProgress:downloadProgress
success:success
failure:failure];
[dataTask resume];
return dataTask;
}

NSURLSessionTask 创建和配置完毕之后,它并不会主动执行,而是需要我们主动调用 resume 方法,NSURLSessionTask 才会开始执行。

6. 网络请求回调

NSURLSessionDelegate 方法

NSURLSessionTaskDelegate 方法

AFN 里面有关 NSURLSessionDelegate 的回调方法非常的多,这里我们只说和 NSURLSessionTask 相关的部分方法和 KVO 处理来进行说明,其他的大家可以参考源码细看。


KVO监听请求过程.png

收到响应数据.png

请求完成.png

对于我们的 Get请求 来说,我们最关注的莫过于关注请求过程进度,收到响应数据和请求完成这2个回调。

KVO监听的属性值发生变化:

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
// KVO监听的属性值发生变化
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
if ([object isKindOfClass:[NSURLSessionTask class]] || [object isKindOfClass:[NSURLSessionDownloadTask class]]) {
if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesReceived))]) {
NSLog(@"countOfBytesReceived");
// 这个是在Get请求下,网络响应过程中已经收到的数据量
// 已经收到
self.downloadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))]) {
NSLog(@"countOfBytesExpectedToReceive");
// 这个是在Get请求下,网络响应过程中期待收到的数据量
// 期待收到
self.downloadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesSent))]) {
NSLog(@"countOfBytesSent");
// 已经发送
self.uploadProgress.completedUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
} else if ([keyPath isEqualToString:NSStringFromSelector(@selector(countOfBytesExpectedToSend))]) {
NSLog(@"countOfBytesExpectedToSend");
// 期待发送
self.uploadProgress.totalUnitCount = [change[NSKeyValueChangeNewKey] longLongValue];
}
}
else if ([object isEqual:self.downloadProgress]) {
// 下载进度变化
if (self.downloadProgressBlock) {
self.downloadProgressBlock(object);
}
}
else if ([object isEqual:self.uploadProgress]) {
// 上传进度变化
if (self.uploadProgressBlock) {
self.uploadProgressBlock(object);
}
}
}

收到请求响应:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 收到请求响应
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveResponse:(NSURLResponse *)response
completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
NSLog(@"收到请求响应");
// 允许处理服务器的响应,才会继续接收服务器返回的数据
NSURLSessionResponseDisposition disposition = NSURLSessionResponseAllow;
// 是否有收到请求响应的回调Block
if (self.dataTaskDidReceiveResponse) {
// 若有调用该Block
disposition = self.dataTaskDidReceiveResponse(session, dataTask, response);
}
// 是否有请求响应完成的回调Block
if (completionHandler) {
// 若有调用该Block
completionHandler(disposition);
}
}

请求完成:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 请求完成
- (void)URLSession:(NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error {
NSLog(@"请求完成");
// 取出该NSURLSessionTask的代理对象
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];
// NSURLSessionTask任务完成之后,移除该NSURLSessionTask的代理对象
[self removeDelegateForTask:task];
}
// 是否有请求完成的回调Block
if (self.taskDidComplete) {
// 若有调用改Block
self.taskDidComplete(session, task, error);
}
}

因为在配置 NSURLSessionDataTask 对象的时候我们有给 NSURLSessionTask 做了一系列配置,那么当 NSURLSessionDataTask 任务完成之后,我们需要将该 NSURLSessionDataTask 的一系列配置全部清理掉。

这个是我们的配置过程:

1
2
3
4
5
6
7
8
9
10
11
12
13
// 通过task的标识符管理代理
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate
forTask:(NSURLSessionTask *)task
{
NSParameterAssert(task);
NSParameterAssert(delegate);
[self.lock lock];
self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)] = delegate;
[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}

那么对应的清理过程是这样的,就是设置过程中做了什么,在清理过程中就需要去掉什么。

1
2
3
4
5
6
7
8
9
10
11
// 给task移除delegate
- (void)removeDelegateForTask:(NSURLSessionTask *)task {
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:task];
[self.lock lock];
[delegate cleanUpProgressForTask:task];
[self removeNotificationObserverForTask:task];
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}

cleanUpProgressForTask.png

removeNotificationObserverForTask.png

Business logic【AFN POST内部逻辑处理】


请求序列化方法

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
#pragma mark - AFURLRequestSerialization
// 设置Header和请求参数
- (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) {
// 判断header的field是否存在,如果不存在则设置,存在则跳过
if (![request valueForHTTPHeaderField:field]) {
// 设置 header
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];
NSString *query = nil;
if (parameters) {
// 用传进来的自定义block格式化请求参数
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;
}
}
}
// 判断是否是GET/HEAD/DELETE方法, 对于GET/HEAD/DELETE方法,把参数加到URL后面
if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
// 判断是否有参数
if (query && query.length > 0) {
// 拼接请求参数
NSLog(@"query-->%@",query);
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 = @"";
}
// 参数带在body上,大多是POST PUT
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
// 设置Content-Type HTTP头,告诉服务端body的参数编码类型
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
[mutableRequest setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}

如果是 Post 请求,那么请求参数是没有拼接在 URL 上面,而是放在 body 上,这是 Post 和 Get 请求的最大区别了,其他过程和Get 请求并没有太多区别。

总结

AFN发起Get请求主要分为以下步骤:

  • 1.创建NSURLSessionDataTask

  • 2.配置NSURLSessionDataTask

  • 3.设置NSURLSessionDataTask的Delegate

  • 4.调用NSURLSessionDataTaskresume方法开始请求

  • 5.在Delegate的方法里面处理网络请求的各个过程

  • 6.清理NSURLSessionDataTask的配置

其实也就是使用 NSURLSessionDataTask 的步骤,AFN在这几个步骤加了一些封装,让我们的使用更简单。

参考原著:http://www.jianshu.com/p/c36159094e24

Reading


  • 如果在阅读过程中遇到 error || new ideas,希望你能 issue 我,我会及时补充谢谢。

  • 不管谁的博客上面写的 (也包括自己),阅读的你要敢于去验证;如人饮水,冷暖自知;(共勉)。

  • 喜欢可 赞赏 or Star一波;点击左上角关注 或 微众『Codeidea』,在 Demo 更新时收到邮件通知,便捷你的阅读。

🖋 Plain boiled water ln : 本文结束    感谢阅读 ^_^. Need coffee?
👁 At this time suggest 1 minute eye exercises

本文标题:iOS 框架详解—「AFNetworking 框架结构及逻辑处理过程」

文章作者:白开水ln

原始链接:http://plainboiledwaterln.cn/SourceAnnotations/AFNLibraryStructure.html

版权声明: 署名-非商业性使用-禁止演绎 4.0 国际 『微众圈:Codeidea』本博客文章除特别声明外均为原创,如需转载请务必保留原链接(可点击进入的链接)和作者出处,谢谢合作!

「喜欢就留言or赞赏」but「支持不要超过你早餐费的 0.5」 ^_^.