最近重讀了AFNetworking 3.x的源碼,算是溫故而知新吧。也梳理了一些優秀的代碼細節和麪試考點,羅列下來,發現這個庫小而精緻,簡直初學者的寶藏庫。程序員
先說個題外話,閱讀優質的開源代碼庫,絕對是程序員們快速提高自個人有效途徑,而怎樣高效率的去閱讀源碼一樣也是一個問題,不知道有沒有人和我以前同樣,碰到過讀卻是讀了,但總感受收穫不大的狀況。面試
這裏分享一下個人一些讀碼經驗:json
多思考,多拋出問題,好比說設計模式
也能夠關注代碼細節,遇到不熟悉的用法不要放過,多刨根究底才能夯實基礎api
關於AFNetworking
的一些優秀代碼細節,我這裏也整理了一部分,能夠查閱後文瀏覽器
必定要記筆記和總結,能分享更好。安全
參考費曼學習法,我認爲這一點是最好的加深理解和強化記憶的手段。隨着年齡的增大,記憶力會有所衰退,有個筆記可以回顧,能節約大把再次記憶的時間。此外,多與人分享還可以提高本身的影響力,與人交流驗證,也可以爲本身查缺補漏。服務器
仍是說回到AFNetworking
這裏,AF的代碼結構大部分人應該都瞭解,這裏我簡單介紹下。AFNetworking 3.x
的代碼結構比2.x要簡單許多,主要也得益於蘋果優化了網絡相關的api,總體代碼有這麼幾部分:網絡
AFURLSessionManager/AFHTTPSessionManagersession
這裏就是AF代碼的核心了,主要負責網絡請求的發起,回調處理,是在系統網絡相關API上的一層封裝。大部分邏輯是在AFURLSessionManager
裏面處理的,AFHTTPSessionManager
則是專爲HTTP請求提供了一些便利方法。若是須要擴展其餘協議的功能(好比FTP協議),能夠考慮從AFURLSessionManager
建立一個子類。
AFURLRequestSerialization/AFURLResponseSerialization
這兩兄弟主要處理一些參數序列化相關的工做。AFURLRequestSerialization
是將傳入的參數構形成NSURLRequest
,好比自定義的header,一些post或者get參數等等。 AFURLResponseSerialization
主要是將系統返回的NSURLResponse
處理成咱們須要的responseObject,好比json、xml、image等等
AFSecurityPolicy
處理https相關的公鑰和驗證邏輯。目前因爲蘋果ATS的開啓,基本HTTPS已經成爲標配。雖然一般直接使用CA來驗證服務器公鑰的狀況下,不須要咱們額外作什麼配置。可是從這裏出發,順便考察一下HTTPS相關的知識點,感受也比較常見,具體面試題可看下文
AFNetworkReachabilityManager
這個實際上是比較獨立的一個模塊了,提供獲取當前網絡狀態的功能。
UIKit+AFNetworking
這裏主要是經過Category來提供了一下UIkit的便利方法
仔細瞅瞅代碼以後,發現常見的OC基礎知識在AF裏面都有具體應用,挺多仍是面試題考點,這裏也是作個記錄和梳理。
+ (instancetype)defaultInstance { static AFImageDownloader *sharedInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedInstance = [[self alloc] init]; }); return sharedInstance; }
必知必會,經過dispatch_once來保證多線程調用時,只有一個實例被建立。
// 注意是並行隊列 self.requestHeaderModificationQueue = dispatch_queue_create("requestHeaderModificationQueue", DISPATCH_QUEUE_CONCURRENT); // 串行寫,注意barrier block的具體執行時機 dispatch_barrier_sync(self.requestHeaderModificationQueue, ^{ [self.mutableHTTPRequestHeaders setValue:value forKey:field]; }); // 並行讀,注意和上面寫操做同時執行時的執行順序 NSDictionary __block *value; dispatch_sync(self.requestHeaderModificationQueue, ^{ value = [NSDictionary dictionaryWithDictionary:self.mutableHTTPRequestHeaders]; });
必知必會,GCD使用barrier來處理並行讀串行寫問題的具體用法
__weak __typeof(self)weakSelf = self; AFNetworkReachabilityStatusCallback callback = ^(AFNetworkReachabilityStatus status) { __strong __typeof(weakSelf)strongSelf = weakSelf; strongSelf.networkReachabilityStatus = status; if (strongSelf.networkReachabilityStatusBlock) { strongSelf.networkReachabilityStatusBlock(status); } return strongSelf; };
必知必會,weakSelf避免循環引用,strongSelf保證block內部執行過程當中self不會被釋放
其餘用法
NSSecureCoding、kvo、swizzle、NSStream、NSProgress、代碼註釋、pragma mark等等
前面提到閱讀開源庫時,要多思考多提問題,這裏也結合一些面試考題來梳理下
這個知識點應該是AF2.x版本面試官比較喜歡問的了,AF2.x版本有個細節,經過runloop開啓了一個常駐子線程,具體代碼是這樣的:
+ (void)networkRequestThreadEntryPoint:(id)__unused object { @autoreleasepool { [[NSThread currentThread] setName:@"AFNetworking"]; NSRunLoop *runLoop = [NSRunLoop currentRunLoop]; [runLoop addPort:[NSMachPort port] forMode:NSDefaultRunLoopMode]; [runLoop run]; } } + (NSThread *)networkRequestThread { static NSThread *_networkRequestThread = nil; static dispatch_once_t oncePredicate; dispatch_once(&oncePredicate, ^{ _networkRequestThread = [[NSThread alloc] initWithTarget:self selector:@selector(networkRequestThreadEntryPoint:) object:nil]; [_networkRequestThread start]; }); return _networkRequestThread; }
首先,咱們要了解爲什麼要開啓常駐子線程?
NSURLConnection
的接口是異步的,而後會在發起的線程回調。而一個子線程,在同步代碼執行完成以後,通常狀況下,線程就退出了。那麼想要接收到NSURLConnection
的回調,就必須讓子線程至少存活到回調的時機。而AF讓線程常駐的緣由是,當發起多個http請求的時候,會統一在這個子線程進行回調的處理,因此乾脆就讓其一直存活下來。
上面說的通常狀況,子線程執行完任務就會退出,那麼什麼狀況下,子線程可以繼續存活呢?這就涉及到第二個問題了,AF是如何開啓常駐線程的,這裏實際上考察的是runloop的基礎知識。
關於runloop,推薦看下《iOS底層原理總結 - RunLoop》,我的以爲講得很是詳盡。這裏簡單來講,當runloop發現還有source/timer/observer的時候,runloop就不會退出。因此AF這裏就經過給當前runloop添加一個NSMachPort,這個port實際上相對於添加了一個source事件源,這樣子線程的runloop就會一直處於循環狀態,等待別的線程向這個port發送消息,而實際上AF這裏是沒有消息發送到這個port的。
除了AF的這種處理方式,實際上蘋果也提供了回調線程的解決方案:
// NSURLConnection - (void)setDelegateQueue:(nullable NSOperationQueue*) queue // NSURLSession + (NSURLSession *)sessionWithConfiguration:(NSURLSessionConfiguration *)configuration delegate:(nullable id <NSURLSessionDelegate>)delegate delegateQueue:(nullable NSOperationQueue *)queue;
蘋果提供了接口,可讓你制定一個operationQueue供回調執行。因此AF3.x的時候,就直接建立了一個併發度爲1的隊列,來處理回調。
擴展問題:
既然提到了runloop,能夠考慮一下runloop的擴展問題:
咱們若是仔細查看代碼,應該就能得出這樣的結論:manager與session是1對1的關係,AF會在manager初始化的時候建立對應的NSURLSession。一樣,AF也在註釋中寫明瞭能夠提供一個配置好的manager單例來全局複用。
那麼複用manager實際上就是複用了session,而複用session能夠帶來什麼好處呢?
其實iOS9以後,session就開始支持http2.0。而http2.0的一個特色就是多路複用(可參考《Http系列(二) Http2中的多路複用》)。因此這裏複用session其實就是在利用http2.0的多路複用特色,減小訪問同一個服務器時,從新創建tcp鏈接的耗時和資源。
官方文檔也推薦在不一樣的功能場景下,使用不一樣的session。好比:一個session處理普通的請求,一個session處理background請求;1個session處理瀏覽器公開的請求,一個session專門處理隱私請求等等場景。
如今,因爲蘋果ATS的策略,基本都切到HTTPS了,HTTPS的基本原理仍是須要了解一下的,這裏不作介紹,須要的能夠google查閱一下相關文章。
一般,首先咱們要了解中間人攻擊,大致就是黑客經過截獲服務器返回的證書,並僞形成本身的證書,一般咱們使用的Charles/Fiddler等工具實際上就能夠當作中間人攻擊。
解決方案其實也很簡單,就是SSL Pinning
。AFSecurityPolicy
的AFSSLPinningMode
就是相關設置項。
SSL Pinning
的原理就是須要將服務器的公鑰打包到客戶端中,tls驗證時,會將服務器的證書和本地的證書作一個對比,一致的話才容許驗證經過。
typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { AFSSLPinningModeNone, AFSSLPinningModePublicKey, // 只驗證證書中的公鑰 AFSSLPinningModeCertificate, // 驗證證書全部字段,包括有效期以內 };
因爲數字證書存在有效期,內置到客戶端後就存在失效後致使驗證失敗的問題,因此能夠考慮設置爲AFSSLPinningModePublicKey
的模式,這樣的話,只要保證證書續期後,證書中的公鑰不變,就可以經過驗證了。
擴展:
用了SSL Pinning
就安全了嗎?
020 持續更新,精品小圈子每日都有新內容,乾貨濃度極高。
結實人脈、討論技術 你想要的這裏都有!
搶先入羣,跑贏同齡人!(入羣無需任何費用)
BAT大廠面試題、獨家面試工具包,
資料免費領取,包括 數據結構、底層進階、圖形視覺、音視頻、架構設計、逆向安防、RxSwift、flutter,