AFNetworking3.0

研究AFNetworking框架。

AFNetworking3.0

NSURLSession

AFNHTTPSessionManager

AFHTTPSessionManager is a subclass of AFURLSessionManager with convenience methods for making HTTP requests. When a baseURL is provided, requests made with the GET / POST / et al. convenience methods can be made with relative paths.

在.h文件中

遵守NSSecureCoding, NSCopying协议

定义属性

baseURL(NSURL),requestSerializer(AFHTTPRequestSerializer),responseSerializer(AFHTTPResponseSerializer)。

  1. 初始化方法:
    + (instancetype)manager;
    - (instancetype)initWithBaseURL:(nullable NSURL *)url;
    `- (instancetype)initWithBaseURL:(nullable NSURL *)url

    sessionConfiguration:(nullable NSURLSessionConfiguration *)configuration NS_DESIGNATED_INITIALIZER;`
    
  2. 创建HTTP请求
    主要是发送GET,POST(有无下载,downloadProgress是在Session queue中,不在主队列),HEAD,PUT,PATCH,DELETE。

1
2
3
4
5
- (nullable NSURLSessionDataTask *)GET:(NSString *)URLString
parameters:(nullable id)parameters
progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgress
success:(nullable void (^)(NSURLSessionDataTask *task, id _Nullable responseObject))success
failure:(nullable void (^)(NSURLSessionDataTask * _Nullable task, NSError *error))failure;

只有POST请求有多种,需要加constructingBodyWithBlock:(nullable void (^)(id <AFMultipartFormData> formData))block将一个参数和拼接数据到HTTP主体。该block参数是采用AFMultipartFormData协议的对象。

在.m文件中

主要是方法:

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

__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这个方法来实现。
实现了:

  1. NSObject中的description方法来打印出BaseURL,session,和operationQueue。
  2. NSSecureCoding中的supportsSecureCoding,initWithCoder,encodeWithCoder方法。
  3. NSCopying中的copyWithZone方法(HTTPClient)

AFURLSessionManager

AFURLSessionManager创建和管理一个 NSURLSession 对象根据一个特殊的 NSURLSessionConfiguration 对象, 它符合<NSURLSessionTaskDelegate>, <NSURLSessionDataDelegate>, <NSURLSessionDownloadDelegate>, and <NSURLSessionDelegate>.

NSURLSession & NSURLSessionTask Delegate Methods:

NSURLSessionDelegate

- URLSession:didBecomeInvalidWithError:
- URLSession:didReceiveChallenge:completionHandler:
- URLSessionDidFinishEventsForBackgroundURLSession:

NSURLSessionTaskDelegate

-URLSession:willPerformHTTPRedirection:newRequest:completionHandler:
- URLSession:task:didReceiveChallenge:completionHandler:
-URLSession:task:didSendBodyData:totalBytesSent:totalBytesExpectedToSend:
- URLSession:task:needNewBodyStream:
- URLSession:task:didCompleteWithError:

NSURLSessionDataDelegate

URLSession:dataTask:didReceiveResponse:completionHandler:
URLSession:dataTask:didBecomeDownloadTask:
URLSession:dataTask:didReceiveData:
URLSession:dataTask:willCacheResponse:completionHandler:

NSURLSessionDownloadDelegate

URLSession:downloadTask:didFinishDownloadingToURL:
URLSession:downloadTask:didWriteData:totalBytesWritten:totalBytesWritten:totalBytesExpectedToWrite:
URLSession:downloadTask:didResumeAtOffset:expectedTotalBytes:

在.h文件中

遵守协议protocol为 <NSURLSessionDelegate, NSURLSessionTaskDelegate, NSURLSessionDataDelegate, NSURLSessionDownloadDelegate, NSSecureCoding, NSCopying>,对应相应的代理方法。

定义属性

  1. session(NSURLSession),operationQueue(NSOperationQueue).responseSerializer(AFURLResponseSerialization)。
  2. 管理安全的策略:securityPolicy(AFSecurityPolicy).
  3. 管理检测网络的连通性:reachabilityManager(AFNetworkReachabilityManager).
  4. 获得 Session Tasks:tasks,dataTasks,uploadTasks,downloadTasks.
  5. 管理回调的队列:
    completionQueue(dispatch_queue_t),completionGroup(dispatch_group_t).

定义方法:

  1. 初始化方法:
    - (instancetype)initWithSessionConfiguration:(nullable NSURLSessionConfiguration *)configuration;
    - (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks;

  2. 运行Data Tasks:
    `- (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;
    

    `

  3. 运行上传任务:
    `- (NSURLSessionUploadTask )uploadTaskWithRequest:(NSURLRequest )request
             fromFile:(NSURL *)fileURL
             progress:(nullable void (^)(NSProgress *uploadProgress))uploadProgressBlock
    completionHandler:(nullable void (^)(NSURLResponse *response, id _Nullable responseObject, NSError  * _Nullable error))completionHandler;`
    
  4. 运行下载任务:
    `- (NSURLSessionDownloadTask )downloadTaskWithRequest:(NSURLRequest )request
             progress:(nullable void (^)(NSProgress *downloadProgress))downloadProgressBlock
          destination:(nullable NSURL * (^)(NSURL *targetPath, NSURLResponse *response))destination
    completionHandler:(nullable void (^)(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error))completionHandler;`
    
    还有就是把NSURLRequest替换成NSData,
  5. 获取任务的进程(Getting Progress for Tasks):
    - (nullable NSProgress *)uploadProgressForTask:(NSURLSessionTask *)task;还有downloadProgressForTask
  6. 设置Session的代理回调
    - (void)setSessionDidBecomeInvalidBlock:(nullable void (^)(NSURLSession *session, NSError *error))block;
    - (void)setSessionDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
  7. 设置Task的代理回调
    - (void)setTaskNeedNewBodyStreamBlock:(nullable NSInputStream * (^)(NSURLSession *session, NSURLSessionTask *task))block;
    - (void)setTaskWillPerformHTTPRedirectionBlock:(nullable NSURLRequest * (^)(NSURLSession *session, NSURLSessionTask *task, NSURLResponse *response, NSURLRequest *request))block;
    - (void)setTaskDidReceiveAuthenticationChallengeBlock:(nullable NSURLSessionAuthChallengeDisposition (^)(NSURLSession *session, NSURLSessionTask *task, NSURLAuthenticationChallenge *challenge, NSURLCredential * _Nullable __autoreleasing * _Nullable credential))block;
    - (void)setTaskDidSendBodyDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, int64_t bytesSent, int64_t totalBytesSent, int64_t totalBytesExpectedToSend))block;
    - (void)setTaskDidCompleteBlock:(nullable void (^)(NSURLSession *session, NSURLSessionTask *task, NSError * _Nullable error))block;
  8. 设置Data Task 的代理回调
    - (void)setDataTaskDidReceiveResponseBlock:(nullable NSURLSessionResponseDisposition (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLResponse *response))block;
    - (void)setDataTaskDidBecomeDownloadTaskBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSURLSessionDownloadTask *downloadTask))block;
    - (void)setDataTaskDidReceiveDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSData *data))block;
    - (void)setDataTaskWillCacheResponseBlock:(nullable NSCachedURLResponse * (^)(NSURLSession *session, NSURLSessionDataTask *dataTask, NSCachedURLResponse *proposedResponse))block;
    - (void)setDidFinishEventsForBackgroundURLSessionBlock:(nullable void (^)(NSURLSession *session))block;
  9. 设置Download Task的回调方法
    - (void)setDownloadTaskDidFinishDownloadingBlock:(nullable NSURL * _Nullable (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, NSURL *location))block;
    - (void)setDownloadTaskDidWriteDataBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t bytesWritten, int64_t totalBytesWritten, int64_t totalBytesExpectedToWrite))block;
    - (void)setDownloadTaskDidResumeBlock:(nullable void (^)(NSURLSession *session, NSURLSessionDownloadTask *downloadTask, int64_t fileOffset, int64_t expectedTotalBytes))block;
  10. 设置通知Notification

在.m 文件中

主要有是三个@interface和@implement。
一个是AFURLSessionManagerTaskDelegate。
一个是_AFURLSessionTaskSwizzling。
一个是AFURLSessionManager。

  1. AFURLSessionManagerTaskDelegate
    遵守协议<NSURLSessionTaskDelegate, NSURLSessionDataDelegate,NSURLSessionDownloadDelegate>,

NSProgress Tracking

- (void)setupProgressForTask:(NSURLSessionTask *)task
一部分是对代理持有的两个属性 uploadProgress和 downloadProgress设置回调

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
self.uploadProgress.totalUnitCount = task.countOfBytesExpectedToSend;
[self.uploadProgress setCancellable:YES];
[self.uploadProgress setCancellationHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask cancel];
}];
[self.uploadProgress setPausable:YES];
[self.uploadProgress setPausingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask suspend];
}];
if ([self.uploadProgress respondsToSelector:@selector(setResumingHandler:)]) {
[self.uploadProgress setResumingHandler:^{
__typeof__(weakTask) strongTask = weakTask;
[strongTask resume];
}];
}

设置上传的取消暂停,下载一样的道理。
另一个部分是对 task 和 NSProgress的uploadProgress和downloadPreogress属性进行键值观测。
- (void)cleanUpProgressForTask:(NSURLSessionTask *)task
主要是移除download和upload的observer。
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
主要是上面第一个函数中的实现NSURLSessionTask和NSURLSessionDownloadTask的类中下载和上传的的接收和期望接收的字节数,发送和期望发送的字节数。改变进度,并调用 block

NSURLSessionTaskDelegate

- (void)URLSession:(__unused NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error
如果遇到error,dispatch_group_async,如果当前 manager持有 completionGroup或者 completionQueue就在主线程中调用 completionHandler并发送通知(在主线程中)
如果在执行当前 task 时没有遇到错误,那么先对数据进行序列化,然后同样调用 block 并发送通知。

NSURLSessionDataTaskDelegate与NSURLSessionDownloadTaskDelegate

- (void)URLSession:(__unused NSURLSession *)session dataTask:(__unused NSURLSessionDataTask *)dataTask didReceiveData:(NSData *)data
在收到数据后调用。并拼接data.
- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
在下载完成对应文件后调用,并处理下载文件。如果fileManagerError,则发出通知。

  1. _AFURLSessionTaskSwizzling
    主要用到runtime的知识,来交换方法。修改 NSURLSessionTask的 resume和 suspend 方法。这样做的目的是为了在方法 resume或者 suspend被调用时发出通知。具体方法调剂过程是在 + load方法中进行的.a.首先用 NSClassFromString(@”NSURLSessionTask”)
    判断当前部署的 iOS 版本是否含有类 NSURLSessionTask。b.因为 iOS7 和 iOS8 上对于 NSURLSessionTask的实现不同,所以会通过 - [NSURLSession dataTaskWithURL:]方法返回一个 NSURLSessionTask 实例。c .取得当前类 _AFURLSessionTaskSwizzling 中的实现 af_resume。d.如果当前类 currentClass有 resume。
    方法真:使用 swizzleResumeAndSuspendMethodForClass:调剂该类的 resume 和 suspend方法
    假:currentClass = [currentClass superclass]

  2. AFURLSessionManager(最重要的地方)
    a.初始化在- (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration中,初始化会话配置,设置为默认的defaultSessionConfiguration,初始化会话session,并设置会话的代理和代理队列,初始化响应序列化responseSerializer,安全认证securityPolicy,和监控网络状态reachabilityManager。

b. 两通知:- (void)taskDidResume:(NSNotification *)notification- (void)taskDidSuspend:(NSNotification *)notification

c.然后为NSURLSessionTask设置AFURLSessionManagerTaskDelegate,

1
2
3
4
5
6
7
8
- (AFURLSessionManagerTaskDelegate *)delegateForTask:(NSURLSessionTask *)task{
NSParameterAssert(task);
AFURLSessionManagerTaskDelegate *delegate = nil;
[self.lock lock];
delegate = self.mutableTaskDelegatesKeyedByTaskIdentifier[@(task.taskIdentifier)];
[self.lock unlock];
return delegate;
}

AFURLSessionManager就是通过字典 mutableTaskDelegatesKeyedByTaskIdentifier来存储并管理每一个 NSURLSessionTask,它以 taskIdentifier为键存储 task。该方法使用 NSLock
来保证不同线程使用 mutableTaskDelegatesKeyedByTaskIdentifier时,不会出现线程竞争的问题。

d.为DataTask加代理。

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

为uploadTask加代理,为downloadTask加代理。移除代理方法
- (void)removeDelegateForTask:(NSURLSessionTask *)task.
用KVO检测
- (NSArray *)tasksForKeyPath:(NSString *)keyPath
如果无效session,则取消任务:
- (void)invalidateSessionCancelingTasks:(BOOL)cancelPendingTasks
设置response的序列
- (void)setResponseSerializer:(id <AFURLResponseSerialization>)responseSerializer
为任务添加(add)和移除(remove)通知观察者.
- (void)addNotificationObserverForTask:(NSURLSessionTask *)task

管理NSURLSessionTask

先调用

1
2
- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler

方法传入NSURLRequest,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
- (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;
url_session_manager_create_task_safely(^{
dataTask = [self.session dataTaskWithRequest:request];
});

[self addDelegateForDataTask:dataTask uploadProgress:uploadProgressBlock downloadProgress:downloadProgressBlock completionHandler:completionHandler];

return dataTask;
}

返回一个 AFURLSessionManagerTaskDelegate对象,将 completionHandler的
uploadProgressBlock和 downloadProgressBlock 传入该对象并在相应事件发生时进行回调
然后

Reachability

AFNetworkReachabilityManager

主要是网络的可用性,类似于苹果文档的Reachability
AFNetworkReachabilityStatusUnknow= -1,
AFNetworkReachabilityStatusNotReachable = 0,
AFNetworkReachabilityStatusReachableViaWWAN = 1,
AFNetworkReachabilityStatusReachableViaWiFi = 2,

在.h文件中

先是初始化:
+ (instancetype)sharedManager;
+ (instancetype)manager;
+ (instancetype)managerForDomain:(NSString *)domain;
+ (instancetype)managerForAddress:(const void *)address;
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER;
然后开始或停止可用性的监控:
- (void)startMonitoring;
- (void)stopMonitoring;
- (NSString *)localizedNetworkReachabilityStatusString;
再设置网络可用性来改变回调:
- (void)setReachabilityStatusChangeBlock:(nullable void (^)(AFNetworkReachabilityStatus status))block;
最后是一些常量,通知和功能。

在.m文件中:

初始化:
+ (instancetype)sharedManager中用dispatch_once,调用+ (instancetype)manager,

1
2
3
4
5
6
7
8
9
10
11
12
+ (instancetype)managerForDomain:(NSString *)domain {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}
+ (instancetype)managerForAddress:(const void *)address {
SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address);
AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability];
CFRelease(reachability);
return manager;
}

  1. 使用 SCNetworkReachabilityCreateWithAddress或者 SCNetworkReachabilityCreateWithName生成一个 SCNetworkReachabilityRef的引用。
  2. 两个方法会通过一个域名或者一个 sockaddr_in的指针生个 SCNetworkReachabilityRef。
  3. 调用 - [AFNetworkReachabilityManager initWithReachability:]将生成的 SCNetworkReachabilityRef引用传给networkReachability.
  4. 当调用 CFBridgingRelease(reachability)后,会把 reachability桥接成一个 NSObject 对象赋值给self.networkReachability,然后释放原来的 CoreFoundation 对象。
1
2
3
4
5
6
7
8
9
- (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
self = [super init];
if (!self) {
return nil;
}
_networkReachability = CFRetain(reachability);
self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
return self;
}

监控网络状态:

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
- (void)startMonitoring {
[self stopMonitoring];
if (!self.networkReachability) {
return;
}
__weak __typeof(self)weakSelf = self;
AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) {
__strong __typeof(weakSelf)strongSelf = weakSelf;
strongSelf.networkReachabilityStatus = status;
if (strongSelf.networkReachabilityStatusBlock) {
strongSelf.networkReachabilityStatusBlock(status);
}
};
SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL};
SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);
SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{
SCNetworkReachabilityFlags flags;
if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) {
AFPostReachabilityStatusChange(flags, callback);
}
});
}
- (void)stopMonitoring {
if (!self.networkReachability) {
return;
}
SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes);
}

  1. 先调用 - stopMonitoring方法,如果之前设置过对网络状态的监听,使用SCNetworkReachabilityUnscheduleFromRunLoop方法取消之前在 Main Runloop 中的监听.
  2. 创建一个在每次网络状态改变时的回调block,每次回调被调用时,重新设置networkReachabilityStatus的属性,调用networkReachabilityStatus,
  3. 创建一个SCNetworkReachabilityContext,其中的 callback就是上一步中的创建的 block 对象。这里的 AFNetworkReachabilityRetainCallback 和 AFNetworkReachabilityReleaseCallback
    都是非常简单的 block,在回调被调用时,只是使用 Block_copy和 Block_release
    这样的宏。传入的 info
    会以参数的形式在 AFNetworkReachabilityCallback
    执行时传入
    static const void AFNetworkReachabilityRetainCallback(const void info) { return Block_copy(info); }
    static void AFNetworkReachabilityReleaseCallback(const void *info) { if (info) { Block_release(info); } }
  4. 当目标的网络状态改变时,会调用传入的回调
  5. 在 Main Runloop 中对应的模式开始监控网络状态。
  6. 取当前的网络状态,调用 callback
    其中SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context);回调了AFNetworkReachabilityManager的AFNetworkReachabilityCallback。
    总结:
    AFNetworkReachabilityManager实际上只是一个对底层 SystemConfiguration库中的 C 函数封装的类,它为我们隐藏了 C 语言的实现,提供了统一的 Objective-C 语言接口。
    它是 AFNetworking中一个即插即用的模块

Security

AFSecurityPolicy

为了阻止中间人攻击,以及其它漏洞的工具。

用枚举设置了验证服务器被信任的方式SSL的类型有None,PublicKey,还有Certificate。
AFSSLPinningModeNone是默认的认证方式,只会在系统的信任的证书列表中对服务端返回的证书进行验证
AFSSLPinningModeCertificate需要客户端预先保存服务端的证书
AFSSLPinningModePublicKey也需要预先保存服务端发送的证书,但是这里只会验证证书中的公钥是否正确
遵守<NSSecureCoding, NSCopying>。

从bundle获取证书,并返回

+ (NSSet <NSData *> *)certificatesInBundle:(NSBundle *)bundle;

获取特殊的安全策略

+ (instancetype)defaultPolicy;

初始化

+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode;
+ (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet <NSData *> *)pinnedCertificates;
初始化方法时,主要目的是设置验证服务器是否受信任的方式

验证服务端是否信任

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
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(NSString *)domain
{
if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) {
NSLog(@"In order to validate a domain name for self signed certificates, you MUST use pinning.");
return NO;
}
NSMutableArray *policies = [NSMutableArray array];
if (self.validatesDomainName) {
[policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)];
} else {
[policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()];
}
SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies);
if (self.SSLPinningMode == AFSSLPinningModeNone) {
return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust);
} else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) {
return NO;
}
switch (self.SSLPinningMode) {
case AFSSLPinningModeNone:
default:
return NO;
case AFSSLPinningModeCertificate: {
NSMutableArray *pinnedCertificates = [NSMutableArray array];
for (NSData *certificateData in self.pinnedCertificates) {
[pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)];
}
SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates);

if (!AFServerTrustIsValid(serverTrust)) {
return NO;
}
// obtain the chain after being validated, which *should* contain the pinned certificate in the last position (if it's the Root CA)
NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust);
for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]) {
if ([self.pinnedCertificates containsObject:trustChainCertificate]) {
return YES;
}
}
return NO;
}
case AFSSLPinningModePublicKey: {
NSUInteger trustedPublicKeyCount = 0;
NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust);
for (id trustChainPublicKey in publicKeys) {
for (id pinnedPublicKey in self.pinnedPublicKeys) {
if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) {
trustedPublicKeyCount += 1;
}
}
}
return trustedPublicKeyCount > 0;
}
}
return NO;
}
  1. 所以如果没有提供证书或者不验证证书,并且还设置 allowInvalidCertificates为,满足上面的所有条件,说明这次的验证是不安全的,会直接返回 NO.
  2. 设置 policy:如果要验证域名的话,就以域名为参数创建一个 SecPolicyRef,否则会创建一个符合 X509 标准的默认SecPolicyRef对象
  3. 验证证书的有效性:如果只根据信任列表中的证书进行验证,即 self.SSLPinningMode == AFSSLPinningModeNone
    。如果允许无效的证书的就会直接返回 YES
    。不允许就会对服务端信任进行验证。
    如果服务器信任无效,并且不允许无效证书,就会返回 NO.
  4. 根据 SSLPinningMode对服务器信任进行验证
    AFSSLPinningModeNone直接返回 NO
    AFSSLPinningModeCertificate
    AFSSLPinningModePublicKey

Serialization

对发出请求以及接收响应的过程进行序列化,这涉及到两个模块

AFURLRequestSerialization

修改请求(主要是 HTTP 请求)的头部,提供了一些语义明确的接口设置 HTTP 头部字段。
主要用于 AFHTTPSessionManager中,因为它主要用于修改 HTTP 头部

AFURLResponseSerialization(重要)

处理响应的模块,将请求返回的数据解析成对应的格式.这个模块使用在 AFURLSessionManager
也就是核心类。其实也只是个协议
@protocol AFURLResponseSerialization <NSObject, NSSecureCoding, NSCopying>
遵循这个协议的类同时也要遵循 NSObject、NSSecureCoding 和 NSCopying 这三个协议,实现安全编码、拷贝以及 Objective-C 对象的基本行为。
这个协议只有一个必须实现的方法:

1
2
3
- (nullable id)responseObjectForResponse:(nullable NSURLResponse *)response
data:(nullable NSData *)data
error:(NSError * _Nullable __autoreleasing *)error NS_SWIFT_NOTHROW;

在.h文件中中有7个类:
第一个
@interface AFHTTPResponseSerializer : NSObject <AFURLResponseSerialization>剩下的六个AFJSONResponseSerializer,AFXMLParserResponseSerializer,AFXMLDocumentResponseSerializer,AFPropertyListResponseSerializer,AFImageResponseSerializer,AFCompoundResponseSerializer都继承自AFHTTPResponseSerializer。

AFHTTPResponseSerializer

先看一下AFHTTPResponseSerializer的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
+ (instancetype)serializer {
return [[self alloc] init];
}

- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.stringEncoding = NSUTF8StringEncoding;
self.acceptableStatusCodes = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(200, 100)];
self.acceptableContentTypes = nil;
return self;
}

因为是对 HTTP 响应进行序列化,所以这里设置了 stringEncoding为 NSUTF8StringEncoding而且没有对接收的内容类型加以限制。
将 acceptableStatusCodes设置为从 200 到 299 之间的状态码, 因为只有这些状态码表示获得了有效的响应
验证响应的有效性:

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
- (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]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: unacceptable content-type: %@", @"AFNetworking", nil), [response MIMEType]],
NSURLErrorFailingURLErrorKey:[response URL], AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorCannotDecodeContentData userInfo:mutableUserInfo], validationError);
}
responseIsValid = NO;
}
if (self.acceptableStatusCodes && ![self.acceptableStatusCodes containsIndex:(NSUInteger)response.statusCode] && [response URL]) {
NSMutableDictionary *mutableUserInfo = [@{
NSLocalizedDescriptionKey: [NSString stringWithFormat:NSLocalizedStringFromTable(@"Request failed: %@ (%ld)", @"AFNetworking", nil), [NSHTTPURLResponse localizedStringForStatusCode:response.statusCode], (long)response.statusCode],
NSURLErrorFailingURLErrorKey:[response URL],
AFNetworkingOperationFailingURLResponseErrorKey: response,
} mutableCopy];
if (data) {
mutableUserInfo[AFNetworkingOperationFailingURLResponseDataErrorKey] = data;
}
validationError = AFErrorWithUnderlyingError([NSError errorWithDomain:AFURLResponseSerializationErrorDomain code:NSURLErrorBadServerResponse userInfo:mutableUserInfo], validationError);
responseIsValid = NO;
}
}
if (error && !responseIsValid) {
*error = validationError;
}
return responseIsValid;
}

这个方法根据在初始化方法中初始化的属性 acceptableContentTypes 和 acceptableStatusCodes来判断当前响应是否有效。

AFURLResponseSerialization协议的实现

1
2
3
4
5
6
- (id)responseObjectForResponse:(NSURLResponse *)response
data:(NSData *)data
error:(NSError *__autoreleasing *)error{
[self validateResponse:(NSHTTPURLResponse *)response data:data error:error];
return data;
}

主要是调用上面的方法对响应进行验证,然后返回数据,实在是没什么难度。

  1. NSSecureCoding
    对安全协议的实现,就是支持安全编码,根据属性acceptableStatusCodes,acceptableContentTypes然后初始化,然后encode
  2. NSCopying
    实现copy属性

    AFJSONResponseSerializer

    初始化方法只是在调用父类的初始化方法之后更新了 acceptableContentTypes
    属性:
1
2
3
4
5
6
7
8
- (instancetype)init {
self = [super init];
if (!self) {
return nil;
}
self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
return self;
}

这个类中与父类差别最大的就是对 AFURLResponseSerialization协议的实现。

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
- (id)responseObjectForResponse:(NSURLResponse *)response data:(NSData *)data error:(
NSError *__autoreleasing *)error
{
#1: 验证请求
if (![self validateResponse:(NSHTTPURLResponse *)response data:data error:error]) {
if (!error || AFErrorOrUnderlyingErrorHasCodeInDomain(*error, NSURLErrorCannotDecodeContentData, AFURLResponseSerializationErrorDomain)) {
return nil;
}
}
#2: 解决一个由只包含一个空格的响应引起的 bug, 略
#3: 序列化 JSON
id responseObject = nil;
NSError *serializationError = nil;
// Workaround for behavior of Rails to return a single space for `head :ok` (a workaround for a bug in Safari), which is not interpreted as valid input by NSJSONSerialization.
// See https://github.com/rails/rails/issues/1742
BOOL isSpace = [data isEqualToData:[NSData dataWithBytes:" " length:1]];
if (data.length > 0 && !isSpace) {
responseObject = [NSJSONSerialization JSONObjectWithData:data options:self.readingOptions error:&serializationError];
} else {
return nil;
}
if (self.removesKeysWithNullValues && responseObject) {
responseObject = AFJSONObjectByRemovingKeysWithNullValues(responseObject, self.readingOptions);
}
#4: 移除 JSON 中的 null
if (error) {
*error = AFErrorWithUnderlyingError(serializationError, *error); }
return responseObject;
}

然后实现NSSecureCoding,NSCopying与上面类似
其他几个子类实现差不多。

AFURLRequestSerialization

AFURLRequestSerialization的主要工作是对发出的 HTTP 请求进行处理,它有几部分的工作需要完成。
而这个文件中的大部分类都是为 AFHTTPRequestSerializer服务的:

  1. 处理查询的 URL 参数
  2. 设置 HTTP 头部字段
  3. 设置请求的属性
  4. 分块上传

处理查询参数

处理查询参数这部分主要是通过 AFQueryStringPair还有一些 C 函数来完成的,这个类有两个属性 field和 value对应 HTTP 请求的查询 URL 中的参数。在.m文件中,先是它的实现。

1
2
3
4
5
6
@interface AFQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;
- (instancetype)initWithField:(id)field value:(id)value;
- (NSString *)URLEncodedStringValue;
@end

其中URLEncodedStringValue方法会返回
return [NSString stringWithFormat:@"%@=%@", AFPercentEscapedStringFromString([self.field description]), AFPercentEscapedStringFromString([self.value description])];
key=value这种格式,同时使用 AFPercentEscapedStringFromString函数来对 field和 value进行处理,将其中的 :#[]@!$&’()*+,;=等字符转换为百分号表示的形式。
这一部分代码还负责返回查询参数,将 AFQueryStringPair中key和value
转换为以下这种形式:
username=tom&password=123456&hello[world]=helloworld
下面是用FOUNDATION_EXPORT定义的NSArray的数组,有AFQueryStringPairsFromDictionary,AFQueryStringPairsFromKeyAndValue,AFQueryStringFromParameters,AFQueryStringPairsFromDictionary,AFQueryStringPairsFromKeyAndValue。
它的实现主要依赖于一个递归函数 AFQueryStringPairsFromKeyAndValue,如果当前的 value
是一个集合类型的话,那么它就会不断地递归调用自己。

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
NSArray * AFQueryStringPairsFromKeyAndValue(NSString *key, id value) {
NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
if ([value isKindOfClass:[NSDictionary class]]) {
NSDictionary *dictionary = value;
// Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
id nestedValue = dictionary[nestedKey];
if (nestedValue) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
}
}
} else if ([value isKindOfClass:[NSArray class]]) {
NSArray *array = value;
for (id nestedValue in array) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
}
} else if ([value isKindOfClass:[NSSet class]]) {
NSSet *set = value;
for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
[mutableQueryStringComponents addObjectsFromArray:AFQueryStringPairsFromKeyAndValue(key, obj)];
}
} else {
[mutableQueryStringComponents addObject:[[AFQueryStringPair alloc] initWithField:key value:value]];
}
return mutableQueryStringComponents;
}

返回一个数组
[ username=tom, password=123456, hello[world]=helloworld]
得到这个数组之后就会调用 AFQueryStringFromParameters使用 &来拼接它们。

1
2
3
4
5
6
7
NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}

  1. 设置请求的属性
    这个下面有一个AFStreamingMultipartFormData的声明,是多形式的数据流。
    1
    2
    3
    4
    5
    6
    7
    8
    static NSArray * AFHTTPRequestSerializerObservedKeyPaths() {
    static NSArray *_AFHTTPRequestSerializerObservedKeyPaths = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
    _AFHTTPRequestSerializerObservedKeyPaths = @[NSStringFromSelector(@selector(allowsCellularAccess)), NSStringFromSelector(@selector(cachePolicy)), NSStringFromSelector(@selector(HTTPShouldHandleCookies)), NSStringFromSelector(@selector(HTTPShouldUsePipelining)), NSStringFromSelector(@selector(networkServiceType)), NSStringFromSelector(@selector(timeoutInterval))];
    });
    return _AFHTTPRequestSerializerObservedKeyPaths;
    }

在这些属性被设置时,会触发 KVO,然后将新的属性存储在一个名为 mutableObservedChangedKeyPaths的字典中:

1
2
3
4
5
6
7
8
9
10
11
12
13
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(__unused id)object
change:(NSDictionary *)change
context:(void *)context
{
if (context == AFHTTPRequestSerializerObserverContext) {
if ([change[NSKeyValueChangeNewKey] isEqual:[NSNull null]]) {
[self.mutableObservedChangedKeyPaths removeObject:keyPath];
} else {
[self.mutableObservedChangedKeyPaths addObject:keyPath];
}
}
}

然后会在生成NSURLRequest的时候设置这些属性:

1
2
3
4
5
6
7
NSMutableURLRequest *mutableRequest = [[NSMutableURLRequest alloc] initWithURL:url];
mutableRequest.HTTPMethod = method;
for (NSString *keyPath in AFHTTPRequestSerializerObservedKeyPaths()) {
if ([self.mutableObservedChangedKeyPaths containsObject:keyPath]) {
[mutableRequest setValue:[self valueForKeyPath:keyPath] forKey:keyPath];
}
}
  1. 设置HTTP头部字段
    在AFHTTPRequestSerializer的interface文件中提供了一些属性方便我们设置 HTTP 头部字段
    mutableHTTPRequestHeaders.
1
2
3
4
5
6
7
8
9
10
11
12
- (NSDictionary *)HTTPRequestHeaders {
//在设置 HTTP 头部字段时,都会存储到这个可变字典中。而当真正使用时,用这个方法,来获取对应版本的不可变字典。
return [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders];
}
- (void)setValue:(NSString *)value
forHTTPHeaderField:(NSString *)field
{
[self.mutableHTTPRequestHeaders setValue:value forKey:field];
}
- (NSString *)valueForHTTPHeaderField:(NSString *)field {
return [self.mutableHTTPRequestHeaders valueForKey:field];
}

这个类是如何设置一些我们平时常用的头部字段的。首先是 User-Agent,AFHTTPRequestSerializer刚刚初始化时,就会根据当前编译的平台生成一个 userAgent。在iOS,ios_watch还有iOS_VERSION_MIN的平台下生成不同。
之后有方法设置授权头部用用户名和密码,还有清除方法:

1
2
3
4
5
6
7
8
9
10
- (void)setAuthorizationHeaderFieldWithUsername:(NSString *)username
password:(NSString *)password
{
NSData *basicAuthCredentials = [[NSString stringWithFormat:@"%@:%@", username, password] dataUsingEncoding:NSUTF8StringEncoding];
NSString *base64AuthCredentials = [basicAuthCredentials base64EncodedStringWithOptions:(NSDataBase64EncodingOptions)0];
[self setValue:[NSString stringWithFormat:@"Basic %@", base64AuthCredentials] forHTTPHeaderField:@"Authorization"];
}
- (void)clearAuthorizationHeader {
[self.mutableHTTPRequestHeaders removeObjectForKey:@"Authorization"];
}

设置了AFHTTPBodyPart,AFMultipartBodyStream的@interface。

字符串:

#UIKit+AFNetworking

参考:
源码解析

文章目录
  1. 1. AFNetworking3.0
  2. 2. NSURLSession
    1. 2.1. AFNHTTPSessionManager
      1. 2.1.1. 在.h文件中
        1. 2.1.1.1. 定义属性
      2. 2.1.2. 在.m文件中
    2. 2.2. AFURLSessionManager
      1. 2.2.1. NSURLSession & NSURLSessionTask Delegate Methods:
        1. 2.2.1.1. NSURLSessionDelegate
        2. 2.2.1.2. NSURLSessionTaskDelegate
        3. 2.2.1.3. NSURLSessionDataDelegate
        4. 2.2.1.4. NSURLSessionDownloadDelegate
      2. 2.2.2. 在.h文件中
        1. 2.2.2.1. 定义属性
        2. 2.2.2.2. 定义方法:
      3. 2.2.3. 在.m 文件中
        1. 2.2.3.1. NSProgress Tracking
        2. 2.2.3.2. NSURLSessionTaskDelegate
        3. 2.2.3.3. NSURLSessionDataTaskDelegate与NSURLSessionDownloadTaskDelegate
        4. 2.2.3.4. 管理NSURLSessionTask
  3. 3. Reachability
    1. 3.1. AFNetworkReachabilityManager
      1. 3.1.1. 在.h文件中
      2. 3.1.2. 在.m文件中:
  4. 4. Security
    1. 4.1. AFSecurityPolicy
      1. 4.1.1. 从bundle获取证书,并返回
      2. 4.1.2. 获取特殊的安全策略
      3. 4.1.3. 初始化
      4. 4.1.4. 验证服务端是否信任
  5. 5. Serialization
    1. 5.1. AFURLRequestSerialization
    2. 5.2. AFURLResponseSerialization(重要)
      1. 5.2.1. AFHTTPResponseSerializer
      2. 5.2.2. AFURLResponseSerialization协议的实现
      3. 5.2.3. AFJSONResponseSerializer
    3. 5.3. AFURLRequestSerialization
      1. 5.3.1. 处理查询参数
本站总访问量 本站访客数人次 ,