AFNetworking源碼解析與面試考點思考

最近重讀了AFNetworking 3.x的源碼,算是溫故而知新吧。也梳理了一些優秀的代碼細節和麪試考點,羅列下來,發現這個庫小而精緻,簡直初學者的寶藏庫。程序員

開源庫怎麼看?

先說個題外話,閱讀優質的開源代碼庫,絕對是程序員們快速提高自個人有效途徑,而怎樣高效率的去閱讀源碼一樣也是一個問題,不知道有沒有人和我以前同樣,碰到過讀卻是讀了,但總感受收穫不大的狀況。面試

這裏分享一下個人一些讀碼經驗:json

  1. 多思考,多拋出問題,好比說設計模式

    • 總體的代碼結構是怎樣的?類與類之間的關係是怎樣的?爲何要這麼設計?
    • 代碼有沒有涉及到多線程,其線程模型是怎樣的?哪類問題能夠適用這種多線程的方案?
    • 代碼中使用了哪些設計模式?具體是怎麼實現的?
  2. 也能夠關注代碼細節,遇到不熟悉的用法不要放過,多刨根究底才能夯實基礎api

    關於AFNetworking的一些優秀代碼細節,我這裏也整理了一部分,能夠查閱後文瀏覽器

  3. 必定要記筆記和總結,能分享更好。安全

    參考費曼學習法,我認爲這一點是最好的加深理解和強化記憶的手段。隨着年齡的增大,記憶力會有所衰退,有個筆記可以回顧,能節約大把再次記憶的時間。此外,多與人分享還可以提高本身的影響力,與人交流驗證,也可以爲本身查缺補漏。服務器

AFNetworking 3.x的代碼結構

仍是說回到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的便利方法

AF的一些優質代碼細節

仔細瞅瞅代碼以後,發現常見的OC基礎知識在AF裏面都有具體應用,挺多仍是面試題考點,這裏也是作個記錄和梳理。

  • 單例的建立方法
+ (instancetype)defaultInstance {
    static AFImageDownloader *sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
    return sharedInstance;
}

必知必會,經過dispatch_once來保證多線程調用時,只有一個實例被建立。

  • dispatch_sync與dispatch_barrier_sync配合解決並行讀串行寫問題
// 注意是並行隊列
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來處理並行讀串行寫問題的具體用法

  • weakSelf與strongSelf的用法
__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等等

AFNetworking的可能面試考點

前面提到閱讀開源庫時,要多思考多提問題,這裏也結合一些面試考題來梳理下

AFNetworking 2.x怎麼開啓常駐子線程?爲什麼須要常駐子線程?

這個知識點應該是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的擴展問題:

  • 子線程默認有runloop嗎?runloop建立和銷燬的時機又是何時呢?
  • runloop有哪些mode呢?滑動時發現定時器沒有回調,是由於什麼緣由呢?
  • 除了上面這種addPort來保活線程的方法,有無其餘方法呢?

AFURLSessionManager與NSURLSession的關係,每次都須要新建mananger嗎?

咱們若是仔細查看代碼,應該就能得出這樣的結論: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專門處理隱私請求等等場景。

AFSecurityPolicy如何避免中間人攻擊?

如今,因爲蘋果ATS的策略,基本都切到HTTPS了,HTTPS的基本原理仍是須要了解一下的,這裏不作介紹,須要的能夠google查閱一下相關文章。

一般,首先咱們要了解中間人攻擊,大致就是黑客經過截獲服務器返回的證書,並僞形成本身的證書,一般咱們使用的Charles/Fiddler等工具實際上就能夠當作中間人攻擊。

解決方案其實也很簡單,就是SSL PinningAFSecurityPolicyAFSSLPinningMode就是相關設置項。

SSL Pinning的原理就是須要將服務器的公鑰打包到客戶端中,tls驗證時,會將服務器的證書和本地的證書作一個對比,一致的話才容許驗證經過。

typedef NS_ENUM(NSUInteger, AFSSLPinningMode) {
    AFSSLPinningModeNone,
    AFSSLPinningModePublicKey,    // 只驗證證書中的公鑰
    AFSSLPinningModeCertificate,    // 驗證證書全部字段,包括有效期以內
};

因爲數字證書存在有效期,內置到客戶端後就存在失效後致使驗證失敗的問題,因此能夠考慮設置爲AFSSLPinningModePublicKey的模式,這樣的話,只要保證證書續期後,證書中的公鑰不變,就可以經過驗證了。

擴展:

用了SSL Pinning就安全了嗎?

更多文章:

2020年iOS大廠面試題總結(一)
iOS學習棧(將持續更新)上
阿里、字節:一套高效的iOS面試題

推薦👇:

  • 020 持續更新,精品小圈子每日都有新內容,乾貨濃度極高。

  • 結實人脈、討論技術 你想要的這裏都有!

  • 搶先入羣,跑贏同齡人!(入羣無需任何費用)

  • (直接搜索羣號:789143298,快速入羣)
  • 點擊此處,與iOS開發大牛一塊兒交流學習

申請即送:

  • BAT大廠面試題、獨家面試工具包,

  • 資料免費領取,包括 數據結構、底層進階、圖形視覺、音視頻、架構設計、逆向安防、RxSwift、flutter,

     
相關文章
相關標籤/搜索