在開發iOS應用過程當中,如何高效的與服務端API進行數據交換,是一個常見問題。通常開發者都會選擇一個第三方的網絡組件做爲服務,以提升開發效率和穩定性。這些組件把複雜的網絡底層操做封裝成友好的類和方法,而且加入異常處理等。ios
那麼,你們最經常使用的組件是什麼?這些組件是如何提高開發效率和穩定性的?哪一款組件適合本身,是 AFNetworking(AFN)仍是 ASIHTTPRequest(ASI)?幾乎每個iOS互聯網應用開發者都會面對這樣的選擇題,要從這兩個最經常使用的組件裏選出一個好的還真不是那麼容易。segmentfault
單單從兩個控件版本提交的時間節點來看,AFN的第一個提交是2011年的1月1日,那個時候ASI早已經是1.8+的版本了;而當AFN發佈1.0版,2012年10月份的時候,ASI早早的已經中止更新了。這樣看起來,AFN是ASI的繼任者,彷佛不存在以前提到的選擇困難的問題,而事實並不是如此。本文將從用法、功能、性能和原理幾個方面對兩者進行簡單對比,看看兩者之間到底存在着怎樣的區別,到底應該如何選擇。服務器
一、用法對比網絡
首先,從推薦用法上就能夠看出兩者設計理念上大有不一樣。數據結構
圖1,AFN的示例代碼,發起請求(出自:Posts.m)併發
AFN官方推薦的使用方法是,爲一系列相關的請求定義一個HTTPClient,共用一個BaseURL。每次請求把URL中除BaseURL的Path部分作爲參數傳給HTTPClient的靜態方法,並註冊一個Block用於回調。框架
圖2,ASI示例代碼,發起異步請求(出自:ASIHTTPRequestTests.m)異步
ASI推薦使用方法就很是傳統,每個請求都由構造方法初始化一個(共享)實例,經過這個實例配置參數併發起請求。ASI最初使用delegate模式回調,在iOS SDK支持Block以後也提供了註冊Block的實例方法。工具
以上引用的兩段代碼都出自各自項目的示例工程。對比兩段代碼能夠很清楚的看出,一樣是發起一個最普通的異步請求,使用AFN只須要調用一個靜態方法,但代碼可讀性較差;而ASI的示例看起來更清晰,但須要調用多個實例方法才能完成一次請求。AFN的設計更加工程化,或者說對使用者更友好,而ASI的設計更經典,典型的OOP。 性能
除了初級用法上的區別,兩者的高級功能和對擴展的支持也很有不一樣。
二、高級功能
AFN只封裝了一些經常使用功能,知足基本需求,而直接忽略了不少擴展功能。例如:AFN默認沒有封裝同步請求,若是開發者須要使用同步請求,則須要重寫getPath:parameters:success:failure方法,對AFHTTPRequestOperation進行同步處理;而ASI則是直接經過調用一個startSynchronous方法。
此外AFN針對JSON、XML、PList和Image四種數據結構封裝了各自處理器,開發者能夠把處理器註冊到操做隊列中,直接在回調方法中得到格式化之後的數據。在示例工程中就使用了JSON處理器:把AFJSONRequestOperation註冊到操做隊列裏。
圖3,AFN示例代碼,初始化自定義的HTTPClient(出自:AFAppDotNetAPIClient.m)
而ASI在這方面顯得更原始,沒有針對任何數據類型作特別封裝,只是預留了各類接口和工具供開發者自行擴展。ASI比AFN提供更多擴展功能還有一個緣由,它把許多內部用到的功能也抽象成類和方法。例如:
ASIHTTPRequestDataCompressor和ASIHTTPRequestDataDecompressor兩個類,只用於壓縮本地文件,構造POST Body和解壓縮返回數據,但這兩個類仍然被設計爲獨立功能,提供了對多種數據結構進行壓縮和解壓縮的方法。
對比兩者的高級功能和對擴展的支持後,能夠看出AFN把初級功能(或者叫經常使用功能)作到了90分。調用方式夠簡單,處理器夠豐富,使用者用起來能夠算是輕鬆加愉快。但它放棄了對高級功能的支持,要知足較複雜的需求,就要大費周折了,在這方面最多隻有40分。而ASI顯然不知足於作好初級功能,但爲了提供更豐富的可擴展接口,致使初級功能用起來也要花上一些力氣。雖然ASI單獨提供了支持Amazon S3和Rackspace Cloud Files的控件,但對於生在紅旗下的我朝開發者來講基本沒用,因此在初級功能的支持上ASI能得個70分,犧牲了初級功能的易用性,換來的是良好的擴展性,在高級功能的使用上遠遠好於AFN,也能得個70分。
從使用角度對比事後,基本上對這兩個項目有一個總體上的認識,再深刻下去看看兩者的性能如何。
三、性能對比
分別用AFN和ASI進行了測試,測試環境以下:iPhone5,聯通3G信號全滿,室內靜止狀態,請求國內雙線機房獨立服務器的靜態文件,1~20K共20個文件,每一個文件請求20次,記錄從建立請求到徹底下載文件的耗時,結果以下:
圖4,AFN連續訪問1 ~ 20K文件耗時
圖5,ASI連續訪問1 ~ 20K文件耗時
圖4是AFN的記錄圖,綠色爲20次請求中耗時最久的一次,藍色爲耗時最短的一次,黃色爲去除最大值和最小值的18次平均值。從這個圖能夠看出,AFN最開始建立對象耗時近2.5秒,隨後穩定下來,在3K、7K、15K和20K時出現了抖動。圖5是ASI作相同測試的結果,首次建立對象近2.25秒,略優於AFN,一樣在5K、11K、13K、14K和16K發生了一些抖動,但抖動幅度彷佛小於AFN,可見穩定性更好一些。
下邊是把兩者的測試結果放在一塊兒的對比圖,能夠更直觀的比較兩者的區別。
圖6,ASI和AFN耗時最大值對比
圖6的最大值對比能夠更明顯的看出兩者的抖動對比,ASI略好一些。
圖7,ASI和AFN耗時最小值對比
圖7的最小值對比能夠看出,在每個大小的測試中ASI的最佳性能彷佛都要優於AFN。
圖8,ASI和AFN耗時平均值對比
圖8是耗時平均值的對比,更可以說明問題。文件小於12K的測試中ASI的性能優點並無很是明顯,超過12K之後,ASI優點開始明顯起來,每一次請求都要比AFN節約20% ~ 30%,近0.1秒。同時從這張圖上還能夠看出,隨着下載文件變大,請求耗時並非線形增加的,這是因爲一次請求大部分時間都消耗在創建鏈接上,而真正接收數據只佔用了極少時間,這個問題不在本篇文章的討論範圍,因此很少說,有興趣的讀者能夠移步http://segmentfault.com/t/ios進一步討論。
四、原理分析
ASI的性能彷佛全面優於AFN,那下邊從兩者的實現原理上看一下究竟是什麼緣由形成這種差距。ASI基於CFNetwork框架開發,而AFN基於NSURL,底層的區別是致使兩者性能差距的重要緣由之一。
圖9,ASI和AFN以及底層框架的關係
咱們知道全部網絡通訊的基礎是Socket,一個Socket與另外一個鏈接並傳送數據。BSD Socket是一類最多見的Socket抽象接口。
Core Foundation框架中的CFSocket就是基於BSD Socket開發的。它幾乎涵蓋了BSD Socket的所有功能,更重要的是把Socket整合到事件的處理循環中。Core Foundation中較高層的CFStream是基於CFSocket開發的讀寫流支持。
CFNetwork是基於Core Foundation中CFStream的一個底層高性能網絡框架,它由提供基礎服務的CFSocketStream,支持HTTP協議的CFHTTP,基於CFHTTP用於身份認證的CFHTTPAuthentication和支持FTP協議的CFFTP組成。
正如圖9所示,ASI是基於CFHTTP開發的一個組件;而AFN的基礎——NSURL,也是基於CFNetwork開發的。也就是說ASI相比AFN更加底層,這就從必定程度上形成兩者的性能差距。
另外一個方面,雖然兩者都使用NSOperation和NSOperationQueue實現但底層的區別也致使實現方式上有很是大的差異。
ASI的直接操做對象ASIHTTPRequest是NSOperation的子類,實現了NSCopying協議。在initialize和initWithURL:方法中初始化相關屬性並配置一系列請求相關參數默認值。此外,ASIHTTPRequest還提供了一系列的實例方法用來配置請求對象。在異步請求的處理上,ASIHTTPRequest對象初始化結束後,在startAsynchronous方法中把對象加入共享操做隊列。此後,包括建立CFHTTPMessageRef,也就是處理網絡請求的主要對象(事實上是一個指向__CFHTTPMessage結構的指針),在內的全部操做都在ASIHTTPRequest對象所屬的子線程中完成。
AFN的直接操做對象AFHTTPClient不一樣於ASI,是一個實現了NSCoding和NSCopying協議的NSObject子類。AFHTTPClient是一個封裝了一系列操做方法的「工具類」,處理請求的操做類是一系列單獨的,基於NSOperation封裝的,AFURLConnectionOperation的子類。AFN的示例代碼中經過一個靜態方法,使用dispatch_once()的方式建立AFHTTPClient的共享實例,這也是官方建議的使用方法。在建立AFHTTPClient的初始化方法中,建立了OperationQueue並設置一系列參數默認值。在getPath:parameters:success:failure方法中建立NSURLRequest,以NSURLRequest對象實例做爲參數,建立一個NSOperation,並加入在初始化發方中建立的NSOperationQueue。以上操做都是在主線程中完成的。在NSOperation的start方法中,以此前建立的NSURLRequest對象爲參數建立NSURLConnection並開啓連結。
在異步回調的處理上兩者也有區別,ASI採起的是CFHTTP請求完成,直接回調ASIHTTPRequest的實例方法,經過儲存的實例對象記錄的信息完成Delegate模式或Block模式的回調。而AFN則直接使用了NSOperation的completionBlock屬性。
這些實現方式也能夠看出,ASI顯得更加底層,並無過多使用Cocoa框架中已經封裝的API,而AFN則更加實用主義,邏輯簡單清晰,大量使用了框架API。這一點也是形成兩者性能差異的緣由之一。
總結
經過以上的對比,基本能夠這樣評價:AFN適合邏輯簡單的應用,或者更適合開發資源尚不豐富的團隊,由於AFN的易用性要比ASI好不少,而這樣的應用(或團隊)對底層網絡控件的定製化要求也很是低。ASI更適合已經發展了一段時間的應用,或者開發資源相對豐富的團隊,由於每每這些團隊(或他們的應用)已經積累了必定的經驗,不管是產品上仍是技術上的。需求複雜度就是在這種時候高起來,並且底層訂製的需求也愈來愈多,此時AFN就很難知足需求,須要犧牲必定的易用性,使用ASI做爲網絡底層控件。SegmentFault開源客戶端如今被設計爲一款簡單的閱讀客戶端,幾乎沒有定製要求,所以,目前我選擇了AFN做爲網絡控件。
以上對ASI和AFN兩款最經常使用的iOS底層網絡控件作了初步的介紹,要更深刻的瞭解兩款控件,還須要你們繼續研究各自的源碼。你們遇到任何關於iOS的技術問題均可以在http://segmentfault.com/t/ios進行討論。另外你們也能夠持續關注SegmentFault的開源客戶端,與更多的開發者共同探討iOS開發技術。