【原】AFNetworking源碼閱讀(六) 本文轉載請註明出處 —— polobymulberry-博客園 1. 前言 這一篇的想講的,一個就是分析一下AFSecurityPolicy文件,看看AFNetworking的網路安全策略,尤其指HTTPS(大家可以先簡單瞭解下HTTPS)。再一個就是分
【原】AFNetworking源碼閱讀(六)
本文轉載請註明出處 —— polobymulberry-博客園
1. 前言
這一篇的想講的,一個就是分析一下AFSecurityPolicy文件,看看AFNetworking的網路安全策略,尤其指HTTPS(大家可以先簡單瞭解下HTTPS)。再一個就是分析下AFNetworkReachabilityManager文件,看看AFNetworking如何解決網路狀態的檢測。
2. AFSecurityPolicy - 網路安全策略
之前我們在AFURLSessionManager中實現了NSURLSessionDelegate的代理方法- (void)URLSession:didReceiveChallenge:completionHandler:,其中我們提到過,如果iOS客戶端需要向伺服器發送一個憑證(Credential)來確認認證挑戰(authentication challenge),那麼先得使用AFNetworking的安全策略類-AFSecurityPolicy來檢查伺服器端是否可以信任(在authenticationMethod屬性為NSURLAuthenticationMethodServerTrust情況下),而檢查的方式就是通過- [AFSecurityPolicy evaluateServerTrust:forDomain:]這個函數。
2.1 - [AFSecurityPolicy evaluateServerTrust:forDomain:]
// 根據severTrust和domain來檢查伺服器端發來的證書是否可信
// 其中SecTrustRef是一個CoreFoundation類型,用於對伺服器端傳來的X.509證書評估的
// 而我們都知道,數字證書的簽發機構CA,在接收到申請者的資料後進行核對並確定信息的真實有效,然後就會製作一份符合X.509標準的文件。證書中的證書內容包含的持有者信息和公鑰等都是由申請者提供的,而數字簽名則是CA機構對證書內容進行hash加密後得到的,而這個數字簽名就是我們驗證證書是否是有可信CA簽發的數據。
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust
forDomain:(nullable NSString *)domain;
具體函數我們直接看函數源碼:
- (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust forDomain:(NSString *)domain { /**
self.allowInvalidCertificates==YES表示如果此處允許使用自建證書(伺服器自己弄的CA證書,非官方),並且還想驗證domain是否有效(self.validatesDomainName == YES),也就是說你想驗證自建證書的domain是否有效。那麼你必須使用pinnedCertificates(就是在客戶端保存伺服器端頒發的證書拷貝)才可以。但是你的SSLPinningMode為AFSSLPinningModeNone,表示你不使用SSL pinning,只跟瀏覽器一樣在系統的信任機構列表裡驗證服務端返回的證書。所以當然你的客戶端上沒有你導入的pinnedCertificates,同樣表示你無法驗證該自建證書。所以都返回NO。最終結論就是要使用伺服器端自建證書,那麼就得將對應的證書拷貝到iOS客戶端,並使用AFSSLPinningMode或AFSSLPinningModePublicKey if (domain && self.allowInvalidCertificates && self.validatesDomainName && (self.SSLPinningMode == AFSSLPinningModeNone || [self.pinnedCertificates count] == 0)) { // https://developer.apple.com/library/mac/documentation/NetworkingInternet/Conceptual/NetworkingTopics/Articles/OverridingSSLChainValidationCorrectly.html 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) { // 如果需要驗證domain,那麼就使用SecPolicyCreateSSL函數創建驗證策略,其中第一個參數為true表示驗證整個SSL證書鏈,第二個參數傳入domain,用於判斷整個證書鏈上葉子節點表示的那個domain是否和此處傳入domain一致 [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; } else { // 如果不需要驗證domain,就使用預設的BasicX509驗證策略 [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; } // 為serverTrust設置驗證策略,即告訴客戶端如何驗證serverTrust SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); // 如果SSLPinningMode為 AFSSLPinningModeNone,表示你不使用SSL pinning,但是我允許自建證書,那麼返回YES,或者使用AFServerTrustIsValid函數看看serverTrust是否可信任,如果信任,也返回YES if (self.SSLPinningMode == AFSSLPinningModeNone) { return self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust); } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { // 既不允許自建證書,而且使用AFServerTrustIsValid函數又返回NO,那麼該serverTrust就真的不能通過驗證了 return NO; } switch (self.SSLPinningMode) { // 理論上,上面那個部分已經解決了self.SSLPinningMode)為AFSSLPinningModeNone)等情況,所以此處再遇到,就直接返回NO case AFSSLPinningModeNone: default: return NO; // 這個模式表示用證書綁定(SSL Pinning)方式驗證證書,需要客戶端保存有服務端的證書拷貝 // 註意客戶端保存的證書存放在self.pinnedCertificates中 case AFSSLPinningModeCertificate: { NSMutableArray *pinnedCertificates = [NSMutableArray array]; for (NSData *certificateData in self.pinnedCertificates) { // 這裡使用SecCertificateCreateWithData函數對原先的pinnedCertificates做一些處理,保證返回的證書都是DER編碼的X.509證書 [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; } // 將pinnedCertificates設置成需要參與驗證的Anchor Certificate(錨點證書,通過SecTrustSetAnchorCertificates設置了參與校驗錨點證書之後,假如驗證的數字證書是這個錨點證書的子節點,即驗證的數字證書是由錨點證書對應CA或子CA簽發的,或是該證書本身,則信任該證書),具體就是調用SecTrustEvaluate來驗證。 SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); if (!AFServerTrustIsValid(serverTrust)) { return NO; } // 伺服器端的證書鏈,註意此處返回的證書鏈順序是從葉節點到根節點 NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); // 從伺服器端證書鏈的根節點往下遍歷,看看是否有與客戶端的綁定證書一致的,有的話,就說明伺服器端是可信的。因為遍歷順序正好相反,所以使用reverseObjectEnumerator for (NSData *trustChainCertificate in [serverCertificates reverseObjectEnumerator]){ if ([self.pinnedCertificates containsObject:trustChainCertificate]) { return YES; } } return NO; } // AFSSLPinningModePublicKey模式同樣是用證書綁定(SSL Pinning)方式驗證,客戶端要有服務端的證書拷貝,只是驗證時只驗證證書里的公鑰,不驗證證書的有效期等信息。只要公鑰是正確的,就能保證通信不會被竊聽,因為中間人沒有私鑰,無法解開通過公鑰加密的數據。 case AFSSLPinningModePublicKey: { NSUInteger trustedPublicKeyCount = 0; // 從serverTrust中取出伺服器端傳過來的所有可用的證書,並依次得到相應的公鑰 NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); // 依次遍歷這些公鑰,如果和客戶端綁定證書的公鑰一致,那麼就給trustedPublicKeyCount加一 for (id trustChainPublicKey in publicKeys) { for (id pinnedPublicKey in self.pinnedPublicKeys) { if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { trustedPublicKeyCount += 1; } } } // trustedPublicKeyCount大於0說明伺服器端中的某個證書和客戶端綁定的證書公鑰一致,認為伺服器端是可信的 return trustedPublicKeyCount > 0; } } return NO; }
上述函數實現中調用了AFSecurityPolicy的私有方法(註意evaluateServerTrust:forDomain:方法是AFSecurityPolicy比較重要的公開方法),下麵我來逐個分析相應的函數實現。
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust)
static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { BOOL isValid = NO; SecTrustResultType result; // 對照下麵的Require_noErr_Quiet函數解釋,此處errorCode指的就是SecTrustEvaluate(serverTrust, &result)函數的返回值。如果serverTrust評估出錯,那麼就直接執行return isValid,預設isValid為NO。 __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); // 如果SecTrustEvaluate函數評估沒出錯,那麼就看result的結果 // 只有當result為kSecTrustResultUnspecified(此標誌表示serverTrust評估成功,此證書也被暗中信任了,但是用戶並沒有顯示地決定信任該證書),或者當result為kSecTrustResultProceed(此標誌表示評估成功,和上面不同的是該評估得到了用戶認可),這兩者取其一就可以認為對serverTrust評估成功 // 在下麵有對result類型(SecTrustResultType)的簡單講解 isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); _out: return isValid; } // Require_noErr_Quiet是一個巨集定義函數,表示如果errorCode不為0(0表示沒有錯誤),那麼就使用C語言中的go語句,跳到對應exceptionLabel地方開始執行代碼。 #ifndef __Require_noErr_Quiet #define __Require_noErr_Quiet(errorCode, exceptionLabel) \ do \ { \ if ( __builtin_expect(0 != (errorCode), 0) ) \ { \ goto exceptionLabel; \ } \ } while ( 0 ) #endif // kSecTrustResultUnspecified和kSecTrustResultProceed都是SecTrustResultType類型 /** SecTrustResultType中枚舉值的含義包括兩個方面:一個是指評估是否成功,另一個是指該評估結果是不是由用戶決定的。對於是不是由用戶決定的這個問題,上面kSecTrustResultUnspecified和kSecTrustResultProceed就是一個很好的例子 */
static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust)
// 獲取到serverTrust中證書鏈上的所有證書 static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { // 使用SecTrustGetCertificateCount函數t獲取到serverTrust中需要評估的證書鏈中的證書數目,並保存到certificateCount中 CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; // 使用SecTrustGetCertificateAtIndex函數獲取到證書鏈中的每個證書,並添加到trustChain中,最後返回trustChain for (CFIndex i = 0; i < certificateCount; i++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; } return [NSArray arrayWithArray:trustChain]; }
static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust)
// 取出serverTrust中證書鏈上每個證書的公鑰,並返回對應的該組公鑰 static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { // 接下來的一小段代碼和上面AFCertificateTrustChainForServerTrust函數的作用基本一致,都是為了獲取到serverTrust中證書鏈上的所有證書,並依次遍歷,取出公鑰。 SecPolicyRef policy = SecPolicyCreateBasicX509(); CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; for (CFIndex i = 0; i < certificateCount; i++) { SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); SecCertificateRef someCertificates[] = {certificate}; CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); SecTrustRef trust; // 根據給定的certificates和policy來生成一個trust對象 __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); SecTrustResultType result; // 使用SecTrustEvaluate來評估上面構建的trust __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); // 如果該trust符合X.509證書格式,那麼先使用SecTrustCopyPublicKey獲取到trust的公鑰,再將此公鑰添加到trustChain中 [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; _out: // 註意釋放資源 if (trust) { CFRelease(trust); } if (certificates) { CFRelease(certificates); } continue; } CFRelease(policy); // 返回對應的一組公鑰 return [NSArray arrayWithArray:trustChain]; }
2.2 + [AFSecurityPolicy policyWithPinningMode:withPinnedCertificates:]
AFSecurityPolicy中還有一些關於初始化的函數,比較重要的就數+ [AFSecurityPolicy policyWithPinningMode:withPinnedCertificates:]這個函數了。
// 初始化AFSecurityPolicy對象的SSLPinningMode和pinnedCertificates兩個屬性 + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode withPinnedCertificates:(NSSet *)pinnedCertificates { AFSecurityPolicy *securityPolicy = [[self alloc] init]; securityPolicy.SSLPinningMode = pinningMode; [securityPolicy setPinnedCertificates:pinnedCertificates]; return securityPolicy; } // 此函數設置securityPolicy中的pinnedCertificates屬性 // 註意還將對應的self.pinnedPublicKeys屬性也設置了,該屬性表示的是對應證書的公鑰(與pinnedCertificates中的證書是一一對應的) - (void)setPinnedCertificates:(NSSet *)pinnedCertificates { _pinnedCertificates = pinnedCertificates; if (self.pinnedCertificates) { NSMutableSet *mutablePinnedPublicKeys = [NSMutableSet setWithCapacity:[self.pinnedCertificates count]]; for (NSData *certificate in self.pinnedCertificates) { id publicKey = AFPublicKeyForCertificate(certificate); if (!publicKey) { continue; } [mutablePinnedPublicKeys addObject:publicKey]; } self.pinnedPublicKeys = [NSSet setWithSet:mutablePinnedPublicKeys]; } else { self.pinnedPublicKeys = nil; } }
static id AFPublicKeyForCertificate(NSData *certificate)
// 此函數沒什麼特別要提及的,和AFPublicKeyTrustChainForServerTrust實現的原理基本一致 // 區別僅僅在該函數是返回單個證書的公鑰(所以傳入的參數是一個證書),而AFPublicKeyTrustChainForServerTrust返回的是serverTrust的證書鏈中所有證書公鑰 static id AFPublicKeyForCertificate(NSData *certificate) { id allowedPublicKey = nil; SecCertificateRef allowedCertificate; SecCertificateRef allowedCertificates[1]; CFArrayRef tempCertificates = nil; SecPolicyRef policy = nil; SecTrustRef allowedTrust = nil; SecTrustResultType result; // 因為此處傳入的certificate參數是NSData類型的,所以需要使用SecCertificateCreateWithData來將NSData對象轉化為SecCertificateRef對象 allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); __Require_Quiet(allowedCertificate != NULL, _out); allowedCertificates[0] = allowedCertificate; tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); policy = SecPolicyCreateBasicX509(); __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); _out: if (allowedTrust) { CFRelease(allowedTrust); } if (policy) { CFRelease(policy); } if (tempCertificates) { CFRelease(tempCertificates); } if (allowedCertificate) { CFRelease(allowedCertificate); } return allowedPublicKey; }
3. AFNetworkReachabilityManager
AFNetworkingReachabilityManager是我使用AFNetworking的時候,第一個接觸到的類。當時主要是用這個類做一些網路狀態判斷。比如我當時做一個視頻類的app時,考慮到網路如果是2G/3G/4G,那麼最好是有一個switchButton來讓用戶選擇是否使用2G/3G/4G來觀看線上視頻,此時你就需要判斷當前網路狀態,如果用戶不允許2G/3G/4G網路觀看線上視頻,而此時手機網路又是2G/3G/4G網路,就得提示用戶當前網路不支持播放線上視頻。
該類的使用方法很簡單,我們看一個簡單例子:
// 設置networkReachabilityStatusBlock,根據不同網路狀態,用戶自定義處理方式 AFNetworkReachabilityManager *manager = [AFNetworkReachabilityManager sharedManager]; [manager setReachabilityStatusChangeBlock:^(AFNetworkReachabilityStatus status) { NSLog(@"network status '%@'", AFStringFromNetworkReachabilityStatus(status)); }]; // 啟動網路監聽 [manager startMonitoring];
其實networkReachabilityStatusBlock應該都好理解。關鍵是這個startMonitoring方法。大家是不是覺得很神奇,感覺就像app在系統後臺有一個迴圈,不停地檢測網路狀態。那麼具體是怎麼做到的呢,這裡先透露點:代碼的核心使用了iOS提供的一系列SCNetworkReachability文件中的方法。
- (void)startMonitoring { // 先停止之前的網路監聽 [self stopMonitoring]; // networkReachability表示的是需要檢測的網路地址的句柄 if (!self.networkReachability) { return; } __weak __typeof(self)weakSelf = self; // 根據網路狀態status來設置網路狀態監聽的回調函數callback AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } }; /** context是一個結構體 typedef struct { // 創建一個SCNetworkReachabilityContext結構體時,需要調用SCDynamicStore的創建函數,而此創建函數會根據version來創建出不同的結構體,SCNetworkReachabilityContext對應的version是0 CFIndex version; // 下麵兩個block(release和retain)的參數就是info,此處表示的是網路狀態處理的回調函數 void * __nullable info; // 該retain block用於對info進行retain,下麵那個AFNetworkReachabilityRetainCallback核心就是調用了Block_copy(用於retain一個block函數,即在堆空間新建或直接引用一個block拷貝) const void * __nonnull (* __nullable retain)(const void *info); // 該release block用於對info進行release,下麵那個AFNetworkReachabilityReleaseCallback核心就是調用了Block_release(用於release一個block函數,即將block從堆空間移除或移除相應引用) void (* __nullable release)(const void *info); // 提供info的description,此處調用為NULL CFStringRef __nonnull (* __nullable copyDescription)(const void *info); } SCNetworkReachabilityContext; */ SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; /** // 給客戶端指定對應target(該參數和需要檢測網路狀況的地址有一定關聯,此處使用的是self.networkReachability),然後當這個target的網路狀態變化時,告之SCNetworkReachabilityCallBack對象callout處理(此處使用的是AFNetworkReachabilityCallback),另外callout中使用到的參數包括target和context提供的info。 Boolean SCNetworkReachabilitySetCallback ( SCNetworkReachabilityRef target, SCNetworkReachabilityCallBack __nullable callout, SCNetworkReachabilityContext * __nullable context ) __OSX_AVAILABLE_STARTING(__MAC_10_3,__IPHONE_2_0); */ SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); /** 此處表示在main RunLoop中以kCFRunLoopCommonModes形式處理self.networkingReachability */ SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); // 在後臺檢測self.networkingReachability的網路狀態,並使用SCNetworkReachabilityGetFlags函數返回產生的flag,註意此處flag表示的就是網路的狀態,後面會詳細介紹每種flag對應的狀態是什麼 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ SCNetworkReachabilityFlags flags; if (SCNetworkReachabilityGetFlags(self.networkReachability, &flags)) { // AFPostReachabilityStatusChange函數就是先將flags轉化為對應的AFNetworkReachabilityStatus變數,然後給我們的callback處理,後面會詳解此函數 AFPostReachabilityStatusChange(flags, callback); } }); }
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block)
static void AFPostReachabilityStatusChange(SCNetworkReachabilityFlags flags, AFNetworkReachabilityStatusBlock block) { // 使用AFNetworkReachabilityStatusForFlags函數將flags轉化為status,提供給下麵block使用 AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); dispatch_async(dispatch_get_main_queue(), ^{ if (block) { block(status); } // 對於用戶,可以使用KVO來觀察status的變化,隨後用戶可以根據傳過來的userInfo[AFNetworkingReachabilityNotificationStatusItem]獲取到相應的status NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; }); } static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { // 該網路地址可達 BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); // 該網路地址雖然可達,但是需要先建立一個connection BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); // 該網路雖然也需要先建立一個connection,但是它是可以自動去connect的 BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); // 不需要用戶交互,就可以connect上(用戶交互一般指的是提供網路的賬戶和密碼) BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); // 如果isReachable==YES,那麼就需要判斷是不是得先建立一個connection,如果需要,那就認為不可達,或者雖然需要先建立一個connection,但是不需要用戶交互,那麼認為也是可達的 BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); // AFNetworkReachabilityStatus就四種狀態Unknown、NotReachable、ReachableViaWWAN、ReachableViaWiFi,這四種狀態字面意思很好理解,這裡就不贅述了 AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; if (isNetworkReachable == NO) { status = AFNetworkReachabilityStatusNotReachable; } #if TARGET_OS_IPHONE else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { status = AFNetworkReachabilityStatusReachableViaWWAN; } #endif else { status = AFNetworkReachabilityStatusReachableViaWiFi; } return status; }
此時AFNetworking如何判斷網路狀態的思路基本也理清楚了:先使用SCNetworkReachability相關函數得到網路狀態,不過此時的網路狀態還需要放到AFNetworking中封裝一層,以提供適合用戶使用的API(如isReachable、isReachableViaWWAN、isReachableViaWiFi),對於不同的網路狀態,用戶只需要定義自己的block進行處理就行。
最後,不知道大家對_networkReachability這個屬性值是否有疑惑:源碼中定義了一個sharedManager,那麼sharedManager中的_networkReachability是如何設置的呢?一圖以蔽之,我就不贅述了。
4. 總結
這一篇作為AFNetworking源碼閱讀系列的最後一篇文章到此結束了。雖然還有部分UIKit+AFNetworking的內容沒說,但是基本也沒什麼難度了,圖片部分可以參考我之前寫的SDWebImage源碼分析。而其他UIKit部分,參考AFNetworking源碼閱讀第一篇文章中對AFNetworkActivityIndicatorManager和UIRefreshControl+AFNetworking的解讀,因為基本思路都是一樣的。
5. 參考文章
- iOS安全系列之一:HTTPS
- iOS安全系列之二:HTTPS進階
- 通讀AFN③--HTTPS訪問控制(AFSecurityPolicy),Reachability(AFNetworkReachabilityManager)