您的位置:首页 > 移动开发 > IOS开发

AFNetworking源码简析

2017-07-21 00:00 197 查看
AFNetworking
基本是苹果开发中网络请求库的标配,它是一个轻量级的网络库,专门针对
iOS
OS X
的网络应用设计,具有模块化的架构和丰富的
APIs
接口,功能强大并且使用简单,深受苹果应用开发人员的喜爱。

本文主要介绍一下
AFNetworking
(版本:
3.1.0
)的模块结构、请求的执行过程、网络状态监测以及网络安全的处理等等,从而对
AFNetworking
的具体功能、执行过程有一个大致的了解,在实际的项目开发过程中,能够更好的进行应用。

一、结构

下面是
AFNetworking
的源码结构图,主要分为:
NSURLSession
核心代码、
Reachability
Security
Serialization
UIKit
5
部分。

AFNetworking
的源码结构图:

<Center>
![图1](http://upload-images.jianshu.io/upload_images/1843940-b101b8580f9b3b6c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>

AFHTTPSessionManager
的依赖关系:

<Center>
![图2](http://upload-images.jianshu.io/upload_images/1843940-7674758339ffdbfc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>

AFNetworking
的网络数据的序列化(
Serialization
)的类结构图:

<Center>
![图3](http://upload-images.jianshu.io/upload_images/1843940-85ce5d8b48246783.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>

NSURLSessionTask
的类结构图:

<Center>
![图4](http://upload-images.jianshu.io/upload_images/1843940-d96b1405c0b74de2.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
</Center>

各部分功能介绍如下:

1.NSURLSession

这是网络请求的核心代码,承担发送请求、接收数据、异常处理等功能,以及请求和响应的数据序列化功能

2.Reachability

用来监控设备的网络状态,只能识别
WWAN
WiFi
这两种网络

3.Security

网络安全的设计,尤其是利用https增强数据安全性

4.Serialization

请求和响应数据的序列化处理

5.UIKit

主要是对一些常用UI控件做网络交互方便的扩展

二、应用

1.网络状态监测

网络状态监测,通过
AFNetworkReachabilityManager
的单例对象设置网络状态变化的
block
,然后通过
AFStringFromNetworkReachabilityStatus
把当前网络状态转换为对应的字符串。一般情况下,设置网络监测的代码放到
AppDelegate.m
里,监测整个项目的网络状态变化。

[[AFNetworkReachabilityManager sharedManager] setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
NSLog(@"Reachability: %@", AFStringFromNetworkReachabilityStatus(status));
}];

[[AFNetworkReachabilityManager sharedManager] startMonitoring];

2.网络安全

HTTPS
相比
HTTP
提高了通信的安全性,而苹果也建议网络请求使用
HTTPS
,所以有必要了解一下
HTTPS
的工作过程(HTTPS通信流程介绍)。而
AFNetworking
中的
AFSecurityPolicy
类用来验证证书是否有效,从而防止被中间人攻击。在实际开发中,会在
AFURLSessionManager
类的初始化方法里使用默认的安全设置:

不允许无效或过期的证书

验证domain名称

不对证书和公钥进行验证

// AFURLSessionManager.m
- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration {
...
self.securityPolicy = [AFSecurityPolicy defaultPolicy];
...
}
// AFSecurityPolicy.m
+ (instancetype)defaultPolicy {
AFSecurityPolicy *securityPolicy = [[self alloc] init];
securityPolicy.SSLPinningMode = AFSSLPinningModeNone;

return securityPolicy;
}

部分重要属性介绍如下:

@property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode;


a、AFSSLPinningModeNone:不验证服务器的整数,完全信任

b、AFSSLPinningModePublicKey:验证服务器返回的证书中的PublicKey

c、AFSSLPinningModeCertificate:把服务器的返回的证书和本地证书进行验证

@property (nonatomic, strong, nullable) NSSet <NSData *> *pinnedCertificates;


这个属性保存着所有的可用做校验的证书的集合

@property (nonatomic, assign) BOOL allowInvalidCertificates;


是否允许无效或过期的证书,默认是不允许

@property (nonatomic, assign) BOOL validatesDomainName;


是否验证证书中的域名domain,默认是验证

3.HTTP请求

HTTP
请求在开发中常用的就是
GET
POST
这两种类型,针对这两种请求方式的区别请参考HTTP协议格式详解。本文从源码实现的角度,不会详细介绍各个类的功能及依赖关系,只是面向应用的角度来分析一下这两种请求如何实现的:

整个网络协议的流程图:



GET/POST

AFNetworking
发起的网络请求,
GET
POST
的差异就是在设置请求的
URL
HTTPBody
时的差异。下面根据上面的流程图详细介绍一下具体的代码实现:

步骤一:发起网络请求

通过
requestSerializer.timeoutInterval
设置请求超时时间

通过
responseSerializer.acceptableContentTypes
设置客户端可接收的数据类型

AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];

manager.requestSerializer.timeoutInterval = 10;
manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", @"text/html", @"text/plain", nil];

[manager GET:@"https://app.chaoaicai.com/api/todayApi/discoveryInfo.app" parameters:@{@"name": @"kelvin"} progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id  _Nullable responseObject) {
NSLog(@"%@", responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
NSLog(@"%@", error);
}];

步骤二、三:创建NSMutableURLRequest 和 NSURLSessionDataTask

- (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
{
...
// 创建Request
NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError];
...
// 创建dataTask
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) {
...
}];
...
}

步骤四:设置AFURLSessionManager的代理

- (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 {

__block NSURLSessionDataTask *dataTask = nil;
// 创建dataTask
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});
// 设置dataTask的代理
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

return dataTask;
}

步骤五:调用NSURLSessionDataTask的resume方法开始网络请求

通过调用
NSURLSessionTask
resume
来启动任务,也可以调用
suspend
来挂起任务

- (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
{

...
[dataTask resume];
...
}

步骤六:AFURLSessionManagerTaskDelegat的方法处理回调数据

这个方法里主要功能是处理接收到的数据,调用保存的任务完成的
block
,并发送接收完成的通知

- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wgnu"
__strong AFURLSessionManager *manager = self.manager;

__block id responseObject = nil;

__block NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
userInfo[AFNetworkingTaskDidCompleteResponseSerializerKey] = manager.responseSerializer;
...

if(error){
...
} else {
...
}
}

步骤7:清理NSURLSessionDataTask配置

删除
NSURLSessionDataTask
的配置信息,如:删除监控收发数据进度的通知、任务开启或挂起的通知、从
mutableTaskDelegatesKeyedByTaskIdentifier
中删除任务的
delegate
等等。之后,回调用户设置的
block
(成功或者失败的回调),等
block
执行结束后,就结束这个
GET
请求。

- (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];
// 清理dataTask的配置信息
[self removeDelegateForTask:task];
}

if (self.taskDidComplete) {
// 回调用户的block
self.taskDidComplete(session, task, error);
}
}

// 清理dataTask配置
- (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];
}

下面介绍一下
GET
POST
在请求时,封装
URL
HTTPBody
的差别:

GET请求

parameters
字典转换为
key=value&key=value
的形式,并附加在用户设置的
url
的后面作为请求的
url


POST请求

parameters
字典转换为
key=value&key=value
的形式,然后添加到
HTTPBody
里面作为请求体发送

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request
withParameters:(id)parameters
error:(NSError *__autoreleasing *)error
{
// 添加用户配置的header的key-value值
[self.HTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
if (![request valueForHTTPHeaderField:field]) {
[mutableRequest setValue:value forHTTPHeaderField:field];
}
}];

...

if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
// GET 请求
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
// POST 请求
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;
}

4.普通请求(直接用NSURLSession)

AFNetworking
除了直接用
GET
POST
发送请求外,还可以直接用
AFURLSessionManager
发送网络请求,而
GET
POST
内部其实也是调用的
AFURLSessionManager
,这样我们也可以手动设置任务的开启或挂起。

下面是
AFNetworking
Github
的示例代码,关于上传或下载代码,请参考AFNetworking的介绍

NSURLSessionConfiguration *configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:configuration];

NSURL *URL = [NSURL URLWithString:@"http://fishbay.cn"];
NSURLRequest *request = [NSURLRequest requestWithURL:URL];

NSURLSessionDataTask *dataTask = [manager dataTaskWithRequest:request completionHandler:^(NSURLResponse *response, id responseObject, NSError *error) {
if (error) {
NSLog(@"Error: %@", error);
} else {
NSLog(@"%@ %@", response, responseObject);
}
}];
[dataTask resume];


至此,AFNetworking的分析到此结束,本文只是从应用的角度分析了一下源码的实现,如有不足之处,欢迎指正。(本文部分图片来自互联网,版权归原作者所有)

参考资料

AFNetworking库下载地址

AFNetworking3.0源码解析1

AFNetworking3.0源码解析2

iOS网络框架-AFNetworking3.1.0源码解读

AFNetworking源码阅读系列

AFNetworking 3.0 源码解读(二)之 AFSecurityPolicy
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ios AFN