AFNetworking

1.导入库

导入 AFN 的 Podfile:

1
2
3
4
5
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
target 'TargetName' do
pod 'AFNetworking', '~> 3.0'
end

写这篇文章时最新的版本是AFNetworking (3.1.0)。

2.模块划分

模块 作用
NSURLSession 发起请求、上传、下载任务,处理回调数据
Serialization 请求响应的序列化、拼接参数
Reachability 监听网络状态
Security 网络安全策略、证书校验
UIKit 视图+分类,控件中网络资源的下载

3.NSURLSession

1.AFURLSessionManager

网络请求的基类,它维护了一个NSURLSession对象,主要功能包括:

  • Init & 配置网络请求的安全策略、队列、锁。
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
- (instancetype)initWithSessionConfiguration:
(NSURLSessionConfiguration *)configuration
{
//略..
if (!configuration) {
configuration = [NSURLSessionConfiguration defaultSessionConfiguration];
}
self.sessionConfiguration = configuration;

//回调所在队列
self.operationQueue = [[NSOperationQueue alloc] init];
//并发数 = 1(串行)
/*按照URLSession接口的要求,其回调所在队列必须是一个串行队列,
*所以队列的并发数须是1,这是为了保证回调的顺序。*/
self.operationQueue.maxConcurrentOperationCount = 1;

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

self.responseSerializer = [AFJSONResponseSerializer serializer];
self.securityPolicy = [AFSecurityPolicy defaultPolicy];

//字典:映射 NSURLSessionTask 与 AFURLSessionManagerTaskDelegate
self.mutableTaskDelegatesKeyedByTaskIdentifier = [[NSMutableDictionary alloc] init];

self.lock = [[NSLock alloc] init];
self.lock.name = AFURLSessionManagerLockName;

//遍历当前正在执行的网络任务,根据任务标识符将各个 block 置为 nil
[self.session getTasksWithCompletionHandler:^(
NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks) {
for (NSURLSessionDataTask *task in dataTasks) {
[self addDelegateForDataTask:task
uploadProgress:nil
downloadProgress:nil
completionHandler:nil];
}
//略..
}];
return self;
}

这里self.operationQueue是请求回调时所在的队列,而非发起请求时的队列。其最大并发数设置为1,是 NSURLSession 为了保证其回调的顺序,要求其delegateQueue必须是一个串行队列:

An operation queue for scheduling the delegate calls and completion handlers. The queue should be a serial queue, in order to ensure the correct ordering of callbacks. If nil, the session creates a serial operation queue for performing all delegate method calls and completion handler calls.

defaultSessionConfiguration的HTTPMaximumConnectionsPerHost属性默认为4,即每个主机默认同时并发请求数为4个。必要时也可自己提供额外的 OperationManager 控制并发情况。

  • 发起网络请求。
1
2
3
4
5
6
//数据任务
- (NSURLSessionDataTask *)dataTaskWithRequest:completionHandler:
//上传任务
- (NSURLSessionUploadTask *)uploadTaskWithRequest:fromFile:progress:completionHandler:
//下载任务
- (NSURLSessionDownloadTask *)downloadTaskWithRequest:progress:destination:completionHandler:
  • 接收数据 & 处理回调。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#pragma mark - NSURLSessionDataDelegate
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
//取出字典中的 Delegate 对象,让它处理后续的业务逻辑。
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:dataTask];
[delegate URLSession:session dataTask:dataTask didReceiveData:data];
//略。。。
}

#pragma mark - NSURLSessionDownloadDelegate
- (void)URLSession:(NSURLSession *)session
downloadTask:(NSURLSessionDownloadTask *)downloadTask
didFinishDownloadingToURL:(NSURL *)location
{
AFURLSessionManagerTaskDelegate *delegate = [self delegateForTask:downloadTask];
//略。。。
[delegate URLSession:session downloadTask:downloadTask didFinishDownloadingToURL:location];
}

2.AFHTTPSessionManager

继承自AFURLSessionManager,将父类中的dataTaskWithRequest进一步细分成 6 大类:

方法 作用
GET 请求指定页面的信息,返回数据实体;
HEAD 与GET类似,不同在于只返回头信息而没有具体数据;
POST 将数据封装在请求体中,上传至指定目录,由服务器创建或更新对应的资源;
PUT 将数据传送给服务器以取代指定文档中的内容;
PATCH 是对PUT方法的补充,用来对已知资源进行局部更新;
DELETE 请求删除指定页面或资源;

对应的6大接口:

1
2
3
4
5
6
7
8
9
10
11
- (NSURLSessionDataTask*)GET:parameters:progress:success:failure:

- (NSURLSessionDataTask*)POST:parameters:...progress:success:failure:

- (NSURLSessionDataTask*)PUT:parameters:success:failure:

- (NSURLSessionDataTask*)DELETE:parameters:success:failure:

- (NSURLSessionDataTask*)HEAD:parameters:success:failure:

- (NSURLSessionDataTask*)PATCH:parameters:success:failure:

6 种任务最终通过下面这个统一入口调用父类AFURLSessionManager中的方法发起网络任务。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//AFHTTPSessionManager.m
- (NSURLSessionDataTask *)dataTaskWithHTTPMethod:
URLString:parameters:uploadProgress:downloadProgress:success:failure:
{
//拼接request参数
NSMutableURLRequest *request = [self.requestSerializer
requestWithMethod:method
URLString:[[NSURL URLWithString:URLString
relativeToURL:self.baseURL] absoluteString]
parameters:parameters
error:&serializationError];
/***略***/
__block NSURLSessionDataTask *dataTask = nil;
//调用父类 AFURLSessionManager 中的方法发起请求
dataTask = [self dataTaskWithRequest:request
uploadProgress:uploadProgress
downloadProgress:downloadProgress
completionHandler:^(/../) {
/**略**/
}];
return dataTask;
}

AFURLSessionManager中对应的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
//AFURLSessionManager.m
- (NSURLSessionDataTask *)dataTaskWithRequest:
uploadProgress:downloadProgress:completionHandler:
{
__block NSURLSessionDataTask *dataTask = nil;
//修复iOS8以下并发队列中任务标识符不唯一的问题(串行+同步)
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];//发起请求
});
//绑定 AFURLSessionManagerTaskDelegate 对象
[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];
return dataTask;
}

发起请求的操作被放在一个名为 url_session_manager_create_task_safely 的 block 里:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
static void url_session_manager_create_task_safely(
dispatch_block_t block) {
if (NSFoundationVersionNumber <
NSFoundationVersionNumber_With_Fixed_5871104061079552_bug) {
dispatch_sync(url_session_manager_creation_queue(), block);
} else {
block();
}
}
static dispatch_queue_t url_session_manager_creation_queue() {
static dispatch_queue_t af_url_session_manager_creation_queue;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
af_url_session_manager_creation_queue = dispatch_queue_create(
"com.alamofire.networking.session.manager.creation",
DISPATCH_QUEUE_SERIAL);
});
return af_url_session_manager_creation_queue;
}

这个 block 是用来修复 iOS8 以下创建 NSURLSession 时taskIdentifier不唯一的问题。

taskIdentifier在后面会被用作保存 AFURLSessionManagerTaskDelegate 对象的 key。所以存在多个 NSURLSession 时,如果此值不唯一,那么 AFURLSessionManagerTaskDelegate 对象与 dataTask 的映射就会出问题。修复方法就是在iOS8以下使用串行+同步的方式发起请求。

3.TaskDelegate

这是一个定义在AFURLSessionManager.m内部的类,内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@interface AFURLSessionManagerTaskDelegate : NSObject 

<NSURLSessionTaskDelegate,
NSURLSessionDataDelegate,
NSURLSessionDownloadDelegate>

@property (nonatomic, weak) AFURLSessionManager *manager;
@property (nonatomic, strong) NSMutableData *mutableData;
@property (nonatomic, strong) NSProgress *uploadProgress;
@property (nonatomic, strong) NSProgress *downloadProgress;
@property (nonatomic, copy) NSURL *downloadFileURL;
@property (nonatomic, copy) AFURLSessionDownloadTaskDidFinishDownloadingBlock
downloadTaskDidFinishDownloading;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock uploadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskProgressBlock downloadProgressBlock;
@property (nonatomic, copy) AFURLSessionTaskCompletionHandler completionHandler;
@end

作用:作为工具类处理 NSURLSession 的数据回调:持有返回的数据、监听进度、回到主线程。

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
#pragma mark - NSURLSessionDataTaskDelegate
- (void)URLSession:(NSURLSession *)session
dataTask:(NSURLSessionDataTask *)dataTask
didReceiveData:(NSData *)data
{
[self.mutableData appendData:data];
}

#pragma mark - NSURLSessionTaskDelegate
- (void)URLSession:(__unused NSURLSession *)session
task:(NSURLSessionTask *)task
didCompleteWithError:(NSError *)error
{
//略..
//dispatch_group 的方式回到主线程执行 block
if (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(), ^{
//执行回调&发送通知 代码略..
});
});
}
}

AFURLSessionManagerTaskDelegate 与 AFURLSessionManager 是如何关联起来的呢?

#1.2章节中说到,AFURLSessionManager 最终会通过统一的入口调用父类中的方法发起请求。在父类发起网络请求后,紧接着就会调用到以下函数:

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
- (void)addDelegateForDataTask:(NSURLSessionDataTask *)dataTask
uploadProgress:(void (^)(NSProgress *uploadProgress)) uploadProgressBlock
downloadProgress:(void (^)(NSProgress *downloadProgress)) downloadProgressBlock
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler
{
AFURLSessionManagerTaskDelegate *delegate = [[AFURLSessionManagerTaskDelegate
alloc] init];
delegate.manager = self;
delegate.completionHandler = completionHandler;

dataTask.taskDescription = self.taskDescriptionForSessionTasks;
[self setDelegate:delegate forTask:dataTask];

delegate.uploadProgressBlock = uploadProgressBlock;
delegate.downloadProgressBlock = downloadProgressBlock;
}
- (void)setDelegate:(AFURLSessionManagerTaskDelegate *)delegate forTask:(NSURLSessionTask *)task
{
[self.lock lock];//保证读写操作的线程安全
self.mutableTaskDelegatesKeyedByTaskIdentifier
[@(task.taskIdentifier)] = delegate; //以task的标识符为键,与delegate进行映射

[delegate setupProgressForTask:task];
[self addNotificationObserverForTask:task];
[self.lock unlock];
}
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task
{
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}

所以,AFURLSessionManagerTaskDelegate是通过 -setDelegate:forTask: 方法,作为 AFURLSessionManager 中mutableTaskDelegatesKeyedByTaskIdentifier字典属性的 value 被保存起来的,其key 是 NSURLSessionTask 的taskIdentifier。读取时通过-delegateForTask: 根据此标识符取出 task 对应的代理。

另外,AFURLSessionManagerTaskDelegate 定义里有个属性:AFURLSessionManager *manager,在函数 -addDelegateForDataTask:uploadProgress:downloadProgress:completionHandler: 里这个 manager 被设置为 self。那么这里存在一个引用链:AFURLSessionManager -> mutableTaskDelegatesKeyedByTaskIdentifier -> AFURLSessionManagerTaskDelegate -> AFURLSessionManager。

这里看似有个相互引用问题,AFN的处理是使用weak声明 manager 属性(见上面的定义),并在请求执行完成后的回调函数里做了如下处理:

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];
if (delegate) {
[delegate URLSession:session task:task didCompleteWithError:error];
[self removeDelegateForTask:task];//注意 就是这一个操作
}
}
- (void)removeDelegateForTask:(NSURLSessionTask *)task
{
[self.lock lock];
//移除 AFURLSessionManagerTaskDelegate 对象
[self.mutableTaskDelegatesKeyedByTaskIdentifier removeObjectForKey:@(task.taskIdentifier)];
[self.lock unlock];
}

AFURLSessionManagerTaskDelegate 对象被从 mutableTaskDelegatesKeyedByTaskIdentifier 字典中移除。那么这个引用链就被打破,也就不存在相互引用问题了。

4.监听进度

TaskDelegate 作为代理会监听任务的进度。其内部是通过 KVO 监听 NSURLSessionTask 的countOfBytesSent等字段实现的:

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
- (void)setupProgressForTask:(NSURLSessionTask *)task 
{
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesReceived))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToReceive))
options:NSKeyValueObservingOptionNew
context:NULL];

[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesSent))
options:NSKeyValueObservingOptionNew
context:NULL];
[task addObserver:self
forKeyPath:NSStringFromSelector(@selector(countOfBytesExpectedToSend))
options:NSKeyValueObservingOptionNew
context:NULL];
//。。。
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if ([object isKindOfClass:[NSURLSessionTask class]] ||
[object isKindOfClass:[NSURLSessionDownloadTask class]]) {
if ([keyPath isEqualToString:
NSStringFromSelector(@selector(countOfBytesReceived))]) {
self.downloadProgress.completedUnitCount = [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);//上传回调
}
}
}

4.Serialization

1.AFURLRequestSerialization

根据用户设置的请求类型,设置 NSURLRequest 的各项参数。

  • 自定义属性参数:
1
2
3
4
5
6
7
@property (nonatomic, assign) NSStringEncoding stringEncoding;
@property (nonatomic, assign) BOOL allowsCellularAccess;
@property (nonatomic, assign) NSURLRequestCachePolicy cachePolicy;
@property (nonatomic, assign) BOOL HTTPShouldHandleCookies;
@property (nonatomic, assign) BOOL HTTPShouldUsePipelining;
@property (nonatomic, assign) NSURLRequestNetworkServiceType networkServiceType;
@property (nonatomic, assign) NSTimeInterval timeoutInterval;
  • 设置请求方法、请求头、请求体:
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
- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError *)error
{
NSURL *url = [NSURL URLWithString:URLString];
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest
alloc] initWithURL:url];
//设置HTTPMethod(GET POST等)
mutableRequest.HTTPMethod = method;
//。。
mutableRequest = [[self requestBySerializingRequest:mutableRequest withParameters:parameters error:error] mutableCopy];
return mutableRequest;
}

- (NSURLRequest *)requestBySerializingRequest:(NSURLRequest *)request withParameters:(id)parameters error:(NSError *)error
{
//。。。
if (parameters) {
if (self.queryStringSerialization) {//如果有自定义的解析方式
NSError *serializationError;
query = self.queryStringSerialization(request, parameters, &serializationError);
//...
}
} else {//默认的解析方式
switch (self.queryStringSerializationStyle) {
case AFHTTPRequestQueryStringDefaultStyle:
query = AFQueryStringFromParameters(parameters);
break;
}
}
}
//如果是GET、HEAD、DELETE方法,参数追加到 URL 尾部
if ([self.HTTPMethodsEncodingParametersInURI containsObject:
[[request HTTPMethod] uppercaseString]]) {
if (query && query.length > 0) {
mutableRequest.URL = [NSURL URLWithString:[[mutableRequest.URL absoluteString] stringByAppendingFormat:mutableRequest.URL.query ? @"&%@" : @"?%@", query]];
}
} else {//POST、PUT方法的参数会放到 HTTPBody 里
if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
[mutableRequest setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
}
//设置HTTPBody
[mutableRequest setHTTPBody:
[query dataUsingEncoding:self.stringEncoding]];
}
return mutableRequest;
}

2.AFHTTPResponseSerializer

校验返回的responsedata

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- (BOOL)validateResponse:(NSHTTPURLResponse *)response
data:(NSData *)data
error:(NSError * __autoreleasing *)error
{
BOOL responseIsValid = YES;
NSError *validationError = nil;
if (response && [response isKindOfClass:[NSHTTPURLResponse class]]) {
if (self.acceptableContentTypes && ![self.acceptableContentTypes containsObject:[response MIMEType]] && !([response MIMEType] == nil && [data length] == 0)) {
if ([data length] > 0 && [response URL]) {
//不支持的 content-type
responseIsValid = NO;
}
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
//返回了不支持的状态码
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}

5.Reachability

使用 SystemConfiguration.framework 中的 SCNetworkReachability 类,监听网络状态:

1
2
3
4
/**
The current network reachability status.
*/
@property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus;

网络状况的枚举定义如下:

1
2
3
4
5
6
7
8
9
typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) {
AFNetworkReachabilityStatusUnknown = -1,//未知
AFNetworkReachabilityStatusNotReachable = 0, //未联网
AFNetworkReachabilityStatusReachableViaWWAN = 1, //蜂窝网络
AFNetworkReachabilityStatusReachableViaWiFi = 2, //WIFI
AFNetworkReachabilityStatusReachableVia2G = 3, //2G
AFNetworkReachabilityStatusReachableVia3G = 4, //3G
AFNetworkReachabilityStatusReachableVia4G = 5, //4G
};

可在 AppDelegate 中开启网络状态的监听:

1
2
3
4
5
6
7
8
9
10
11
12
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//开启AFNetworking网络监测
AFNetworkReachabilityManager *shareMana = [AFNetworkReachabilityManager sharedManager];
[shareMana startMonitoring];
[shareMana setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) {
dispatch_async(dispatch_get_main_queue(), ^{
// ...
});
}];
return YES;
}

6.Security

处理与服务器交互时的验证问题,保证应用与服务器通信时的SSL连接安全。

客户端与服务器进行通信时,服务器会返回一个SSL证书,客户端需要验证证书的颁发机构、公钥、是否过期等信息。SSL Pinning 就是iOS中用来处理此类事务的,它可以按照你的设置,将服务器下发的证书与本地保存的证书进行对比,验证通过才能继续通信。这些设置在 AFN 中分三类:

验证模式 作用
AFSSLPinningModeNone 客户端无条件地信任服务器端返回的证书,不做校验;
AFSSLPinningModePublicKey 只验证服务器端返回的证书中公钥的部分,与本地证书一致才继续;有效期等不做校验(省去过期的麻烦);
AFSSLPinningModeCertificate 验证服务器端返回的证书和本地保存的证书中的所有内容,有一项不符合则验证失败;

AFN 会根据用户设置的验证模式处理 SSL Pinning 验证业务。其中后两种验证模式需要将服务器的证书文件拷贝一份保存到工程目录中。

  • 具体实践:

AFN的 AFHTTPSessionManager 类中有个securityPolicy属性:

1
2
3
4
5
/**
The security policy used by created session to evaluate server trust for secure connections.
*`AFURLSessionManager` uses the `defaultPolicy` unless otherwise specified.
*/
@property (nonatomic, strong) AFSecurityPolicy *securityPolicy;

一般是在基于 AFHTTPSessionManager 的单例类中给 securityPolicy 属性指定一个验证模式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@interface TLHttpAPIClient : AFHTTPSessionManager
@end

@implementation TLHttpAPIClient
+ (instancetype)sharedClient {
static TLHttpAPIClient *sharedClient = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedClient = [TLHttpAPIClient new];
// 设置证书验证模式
AFSecurityPolicy *securityPolicy = [AFSecurityPolicy policyWithPinningMode: AFSSLPinningModeCertificate];
// 证书在bundle中的路径
NSString *cerPath = [[NSBundle mainBundle] pathForResource:@"Demo" ofType:@"cer"];
NSData *cerData = [NSData dataWithContentsOfFile:cerPath];
[securityPolicy setPinnedCertificates:@[cerData]];
sharedClient.securityPolicy = securityPolicy;
});
return sharedClient;
}
@end

AFSSLPinningModeNone模式下不用设置证书路径。使用证书进行验证时,注意证书是否过期。

7.小结

源码部分暂时先写到这,UIKit+AFNetworking 部分及其他细节待续~~说说目前对AFN的感受吧。

  • 各模块分工清晰,职责明确,高聚合、低耦合;
  • 请求被封装成6个数据接口和上传下载接口,方便地应对各种场景;
  • 大量使用了异步、block、C函数;

多翻翻 AFN 在Github上的 issue 和解决方案,看人家是怎么解决问题的,会有很多收获~


AFNetworking
https://davidlii.cn/2018/01/31/afn.html
作者
Davidli
发布于
2018年1月31日
许可协议