研究AFN

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。
- (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文件中

主要是方法:

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

  1. 设置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设置回调

 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,则发出通知。

2._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]

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

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

c.然后为NSURLSessionTask设置AFURLSessionManagerTaskDelegate,

- (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加代理。
  • (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

先调用

- (NSURLSessionDataTask *)dataTaskWithRequest:(NSURLRequest *)request
                            completionHandler:(void (^)(NSURLResponse *response, id responseObject, NSError *error))completionHandler```
方法传入NSURLRequest,
  • (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,

+ (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 对象。

  • (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability {
    self = [super init];
    if (!self) {
    return nil;
    }
    _networkReachability = CFRetain(reachability);
    self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown;
    return self;
    }
监控网络状态:
  • (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;`
初始化方法时,主要目的是设置**验证服务器是否受信任的方式**。
### 验证服务端是否信任

  • (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 对象的基本行为。
这个协议只有一个必须实现的方法:

- (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的实现:
  • (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 之间的状态码, 因为只有这些状态码表示**获得了有效的响应**
验证响应的有效性:
  • (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协议的实现

- (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
 属性:

  • (instancetype)init {
    self = [super init];
    if (!self) {
    return nil;
    }
    self.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/json", @"text/javascript", nil];
    return self;
    }```
    这个类中与父类差别最大的就是对 AFURLResponseSerialization协议的实现。
- (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文件中,先是它的实现。

@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
 是一个集合类型的话,那么它就会不断地递归调用自己。

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使用 &来拼接它们。

NSString * AFQueryStringFromParameters(NSDictionary *parameters) {
NSMutableArray *mutablePairs = [NSMutableArray array];
for (AFQueryStringPair *pair in AFQueryStringPairsFromDictionary(parameters)) {
[mutablePairs addObject:[pair URLEncodedStringValue]];
}
return [mutablePairs componentsJoinedByString:@"&"];
}```

  1. 设置请求的属性
    这个下面有一个AFStreamingMultipartFormData的声明,是多形式的数据流。
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的字典中:

  • (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的时候设置这些属性:
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.
- (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的平台下生成不同。
之后有方法设置授权头部用用户名和密码,还有清除方法:

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

参考:
[源码解析](https://github.com/Draveness/iOS-Source-Code-Analyze/blob/master/AFNetworking/%E5%A4%84%E7%90%86%E8%AF%B7%E6%B1%82%E5%92%8C%E5%93%8D%E5%BA%94%20AFURLSerialization%EF%BC%88%E4%B8%89%EF%BC%89.md)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容