iOS AFNetWorking原始碼詳解(一)

發表於2016-07-10

首先來介紹下AFNetWorking,官方介紹如下:

AFNetworking is a delightful networking library for iOS and Mac OS X. It’s built on top of theFoundation URL Loading System, extending the powerful high-level networking abstractions built into Cocoa. It has a modular architecture with well-designed, feature-rich APIs that are a joy to use.

Perhaps the most important feature of all, however, is the amazing community of developers who use and contribute to AFNetworking every day. AFNetworking powers some of the most popular and critically-acclaimed apps on the iPhone, iPad, and Mac.

Choose AFNetworking for your next project, or migrate over your existing projects—you’ll be happy you did!

翻譯過來簡單來說就是

AFNetworking是一個適用於iOS和Mac OS X兩個平臺的網路庫,它是基於Foundation URL Loading System上進行了一套封裝,並且提供了豐富且優美的API介面給使用者使用

AFNetWorking

相信從star數和fork數來看,大家都能明白這個庫是多麼的受歡迎了,所以瞭解這個庫對於一個iOS開發來說是極為重要的!

這個是AFNetworking的github地址:GitHub – AFNetworking/AFNetworking: A delightful networking framework for iOS

在使用前閱讀README是非常重要的,裡面往往包括了這個庫的介紹、安裝和使用等等,對於快速瞭解一個庫來說,這是非常有幫助的


首先我們在AFNetWorking原始碼地址裡download下來,開啟工程檔案,可以看到裡面內容分為兩個部分,一個是AFNetworking,另一個是UIKit+AFNetworking

AFNetWorking_file

很明顯,第一個是用來做網路請求相關的,第二個則是和UI使用相關的,我們先看第一個

在看完標頭檔案和README之後,你會發現AFURLSessionManagerAFHTTPSessionManager是裡面比較重要的兩個類

這裡我先講AFURLSessionManager這個類

首先瀏覽完這個類從API,發現其主要提供了資料的請求、上傳和下載功能

在屬性方面:

通過這四個屬性,我們分別可以拿到總的任務集合、資料任務集合、上傳任務集合和下載任務集合

這個屬性非常重要,註釋裡面寫到,在iOS7中存在一個bug,在建立後臺上傳任務時,有時候會返回nil,所以為了解決這個問題,AFNetworking遵照了蘋果的建議,在建立失敗的時候,會重新嘗試建立,次數預設為3次,所以你的應用如果有場景會有在後臺上傳的情況的話,記得將該值設為YES,避免出現上傳失敗的問題

在對外提供的notification key裡面,使用了FOUNDATION_EXPORT來定義常量,使用FOUNDATION_EXPORTextern或者define有什麼區別呢?

FOUNDATION_EXPORT在c檔案編譯下是和extern等同,在c++檔案編譯下是和extern “C”等同,在32位機的環境下又是另外編譯情況,在相容性方面,FOUNDATION_EXPORT做的會更好。

這裡還提到了效率方面的問題:iOS開發的一些奇巧淫技3


進入到實現檔案裡面,我們可以看到在外部API呼叫dataTask、uploadTask、downloadTask方法實際上都是completionHanlder block返回出來的,但是我們知道網路請求是delegate返回結果的,AF內部做了巧妙的操作,他對每個task都增加代理設定

在設定裡面,每個task會在內部建立AFURLSessionManagerTaskDelegate物件,並設定completionHandler、uploadProgressBlock、downloadProgressBlock回撥

然後delegate物件利用kvo將task對一些方法進行監聽,並且監聽到變化時,通過block返回,將delegate轉成block出去

在原先IM的設計時,因為介面的數量並不多,所以在AsyncSocket的delegate回撥後,我們依舊是採用delegate回撥給業務層,但是隨著介面數量的增加,業務層對於回撥的處理更加困難和不可控,在重構IM的時候,我們也參考學習了AF的做法,我們通過對唯一標識和每個請求做一一繫結,將請求的上下文關聯起來,這樣讓socket長連線的請求的也想http請求一樣,都由block回去,對於業務層的處理也方便更多

setupProgressForTask方法主要是對task和progress設定監聽

在第一個if判斷裡面,object判斷是否是NSURLSessionTask類或者是否是NSURLSessionDownloadTask類,但是進到NSURLSessionDownloadTask的時候,我們可以看到NSURLSessionDownloadTaskNSURLSessionTask的子類,那為什麼還要判斷這個呢?

NSURLSessionTask實際上是Class cluster,通過NSURLSession生成的task返回的並不一定是指定的task型別。因此kindOfClass並不總會生效,具體可以參見AFURLSessionManager.m在load方法中的說明
特定於當前問題,是由於iOS 7上NSCFURLSessionDownloadTask的基類並不是NSCFURLSessionTask,因此isKindOfClass會出錯。檢視對應的commit就可以知道了。

NSURLSessionTaskDelegate的代理裡面,只是做了兩件事情,第一個是獲取資料,將responseSerializer和downloadFileURL或data存到userInfo裡面,第二個是根據error是否為空值,做下一步處理

在有error時,userInfo先儲存error,然後檢查manager是否有completionGroup和completionQueue,沒有的話,就建立一個dispatch_group_t和在主執行緒上做completionHandler的操作,並在主執行緒中傳送一個AFNetworkingTaskDidCompleteNotification通知,這個通知在UIKit+AFNetworking裡UIRefreshControl +AFNetworking裡也會接收到,用來停止重新整理,如果你不使用AF的UI部分,你可以通過接收這個通知來做操作

在沒有error時,會先對資料進行一次序列化操作,然後下面的處理就和有error的那部分一樣了

一開始我們就看到了clang命令,這個的作用是用來消除特定區域的clang的編譯警告,-Wgnu則是消除?:警告,這個是clang的警告message列表Which Clang Warning Is Generating This Message?

再下面兩個則是收到資料和下載檔案的回撥處理

在剛才說到的load方法裡面,對系統的resume和suspend方法進行了替換

替換之後,只是增加了通知處理而已

在呼叫替換和增加方法時候,用到了關鍵字inline,inline是為了防止反彙編之後,在符號表裡面看不到你所呼叫的該方法,否則別人可以通過篡改你的返回值來造成攻擊,iOS安全–使用static inline方式編譯函式,防止靜態分析,特別是在使用swizzling的時候,那除了使用swizzling動態替換函式方法之外,還有別的方法麼?有,修改IMP指標指向的方法,輕鬆學習之 IMP指標的作用 – CocoaChina_讓移動開發更簡單

在+ load方法中,我們又看到了GCC命令,那clang和GCC在使用的時機有沒有什麼區別?通常情況下,在GCC特有的處理或者是在GCC,clang和其他相容GCC的編譯器時,儘量使用#pragma GCC,clang特有的處理時,使用#pragma clang,這個是GCC的message表


看完之後,有個疑問,查了資料也沒有找到:

在NSURLSessionDelegate的URLSession:didReceiveChallenge:completionHandler:方法裡面disposition會對credential物件做非空判斷然後再賦值校驗型別,但是NSURLSessionTaskDelegate的– [URLSession:task:didReceiveChallenge:completionHandler:]方法裡面disposition並不對credential物件做判斷,而是直接就賦值校驗型別,有知道的,歡迎留言交流

相關文章