iOS 中,關於網絡請求的接口自下至上有以下幾層:git
CFSocket CFNetwork ->ASIHttpRequest NSURLConnection ->AFNetworking NSURLSession ->AFNetworking2, Alamofire
• CFSocket 是最底層的接口,只負責 socket 通訊。
• CFNetwork 是基於 CFSocket 等接口的上層封裝,ASIHttpRequest 工做於這一層。
• NSURLConnection 是基於 CFNetwork 的更高層的封裝,提供面向對象的接口,AFNetworking 工做於這一層。
• NSURLSession 是 iOS7 中新增的接口,表面上是和 NSURLConnection 並列的,但底層仍然用到了 NSURLConnection 的部分功能 (好比 com.apple.NSURLConnectionLoader 線程),AFNetworking2 和 Alamofire 工做於這一層。github
下面主要介紹下 NSURLConnection 的工做過程。網絡
一般使用 NSURLConnection 時,你會傳入一個 Delegate,當調用了 [connection start] 後,這個 Delegate 就會不停收到事件回調。實際上,start 這個函數的內部會會獲取 CurrentRunLoop,而後在其中的 DefaultMode 添加了4個 Source0 (即須要手動觸發的Source)。CFMultiplexerSource 是負責各類 Delegate 回調的,CFHTTPCookieStorage 是處理各類 Cookie 的。app
當開始網絡傳輸時,咱們能夠看到 NSURLConnection 建立了兩個新線程:com.apple.NSURLConnectionLoader 和 com.apple.CFSocket.private。其中 CFSocket 線程是處理底層 socket 鏈接的。NSURLConnectionLoader 這個線程內部會使用 RunLoop 來接收底層 socket 的事件,並經過以前添加的 Source0 通知到上層的 Delegate。框架
NSURLConnectionLoader 中的 RunLoop 經過一些基於 mach port 的 Source 接收來自底層 CFSocket 的通知。當收到通知後,其會在合適的時機向 CFMultiplexerSource 等 Source0 發送通知,同時喚醒 Delegate 線程的 RunLoop 來讓其處理這些通知。CFMultiplexerSource 會在 Delegate 線程的 RunLoop 對 Delegate 執行實際的回調。socket
AFURLConnectionOperation 這個類是基於 NSURLConnection 構建的,其但願能在後臺線程接收 Delegate 回調。爲此 AFNetworking 單首創建了一個線程,並在這個線程中啓動了一個 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; }
RunLoop 啓動前內部必需要有至少一個 Timer/Observer/Source,因此 AFNetworking 在 [runLoop run] 以前先建立了一個新的 NSMachPort 添加進去了。一般狀況下,調用者須要持有這個 NSMachPort (mach_port) 並在外部線程經過這個 port 發送消息到 loop 內;但此處添加 port 只是爲了讓 RunLoop 不至於退出,並無用於實際的發送消息。oop
- (void)start { [self.lock lock]; if ([self isCancelled]) { [self performSelector:@selector(cancelConnection) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } else if ([self isReady]) { self.state = AFOperationExecutingState; [self performSelector:@selector(operationDidStart) onThread:[[self class] networkRequestThread] withObject:nil waitUntilDone:NO modes:[self.runLoopModes allObjects]]; } [self.lock unlock]; }
當須要這個後臺線程執行任務時,AFNetworking 經過調用 [NSObject performSelector:onThread:..] 將這個任務扔到了後臺線程的 RunLoop 中。spa
AsyncDisplayKit 是 Facebook 推出的用於保持界面流暢性的框架,其原理大體以下:線程
UI 線程中一旦出現繁重的任務就會致使界面卡頓,這類任務一般分爲3類:排版,繪製,UI對象操做。
排版一般包括計算視圖大小、計算文本高度、從新計算子式圖的排版等操做。
繪製通常有文本繪製 (例如 CoreText)、圖片繪製 (例如預先解壓)、元素繪製 (Quartz)等操做。
UI對象操做一般包括 UIView/CALayer 等 UI 對象的建立、設置屬性和銷燬。
其中前兩類操做能夠經過各類方法扔到後臺線程執行,而最後一類操做只能在主線程完成,而且有時後面的操做須要依賴前面操做的結果 (例如TextView建立時可能須要提早計算出文本的大小)。ASDK 所作的,就是儘可能將能放入後臺的任務放入後臺,不能的則儘可能推遲 (例如視圖的建立、屬性的調整)。
爲此,ASDK 建立了一個名爲 ASDisplayNode 的對象,並在內部封裝了 UIView/CALayer,它具備和 UIView/CALayer 類似的屬性,例如 frame、backgroundColor等。全部這些屬性均可以在後臺線程更改,開發者能夠只經過 Node 來操做其內部的 UIView/CALayer,這樣就能夠將排版和繪製放入了後臺線程。可是不管怎麼操做,這些屬性總須要在某個時刻同步到主線程的 UIView/CALayer 去。
ASDK 仿照 QuartzCore/UIKit 框架的模式,實現了一套相似的界面更新的機制:即在主線程的 RunLoop 中添加一個 Observer,監聽了 kCFRunLoopBeforeWaiting 和 kCFRunLoopExit 事件,在收到回調時,遍歷全部以前放入隊列的待處理的任務,而後一一執行。
具體的代碼能夠看這裏:_ASAsyncTransactionGroup。