研究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)
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容