最近在看runloop時看到不少blog都說AFNetworking,有使用到runloop創(chuàng)建一個子線程并保持線程不斷循環(huán)。

我在我的項目(AF 3.x)中尋找并沒有找到相關(guān)方法,之后在AF 2.x中找到了相關(guān)方法。?
AF 2.x基于NSURLConnection包裝的重要對象,由于iOS9-NSURLConnection已經(jīng)不能使用,AFNetworking在3.x版本中刪除了基于 NSURLConnection API的所有支持。如果項目以前使用過這些API,那么我們需要升級到基于 NSURLSession 的API的AFNetworking的版本。
線程 ?摘自 ( ?AFNetworking2.0源碼解析 ? bang’s blog)
來看看NSURLConnection發(fā)送請求時的線程情況,NSURLConnection是被設(shè)計成異步發(fā)送的,調(diào)用了start方法后,NSURLConnection會新建一些線程用底層的CFSocket去發(fā)送和接收請求,在發(fā)送和接收的一些事件發(fā)生后通知原來線程的Runloop去回調(diào)事件。
NSURLConnection的同步方法sendSynchronousRequest方法也是基于異步的,同樣要在其他線程去處理請求的發(fā)送和接收,只是同步方法會手動block住線程,發(fā)送狀態(tài)的通知也不是通過RunLoop進(jìn)行。
使用NSURLConnection有幾種選擇:
A.在主線程調(diào)異步接口
若直接在主線程調(diào)用異步接口,會有個Runloop相關(guān)的問題
當(dāng)在主線程調(diào)用[[NSURLConnection alloc] initWithRequest:request delegate:self startImmediately:YES]時,請求發(fā)出,偵聽任務(wù)會加入到主線程的Runloop下,RunloopMode會默認(rèn)為NSDefaultRunLoopMode。這表明只有當(dāng)前線程的Runloop處于NSDefaultRunLoopMode時,這個任務(wù)才會被執(zhí)行。但當(dāng)用戶滾動tableview或scrollview時,主線程的Runloop是處于NSEventTrackingRunLoopMode模式下的,不會執(zhí)行NSDefaultRunLoopMode的任務(wù),所以會出現(xiàn)一個問題,請求發(fā)出后,如果用戶一直在操作UI上下滑動屏幕,那在滑動結(jié)束前是不會執(zhí)行回調(diào)函數(shù)的,只有在滑動結(jié)束,RunloopMode切回NSDefaultRunLoopMode,才會執(zhí)行回調(diào)函數(shù)。蘋果一直把動畫效果性能放在第一位,估計這也是蘋果提升UI動畫性能的手段之一。
所以若要在主線程使用NSURLConnection異步接口,需要手動把RunloopMode設(shè)為NSRunLoopCommonModes。這個mode意思是無論當(dāng)前Runloop處于什么狀態(tài),都執(zhí)行這個任務(wù)。
B.在子線程調(diào)同步接口
若在子線程調(diào)用同步接口,一條線程只能處理一個請求,因為請求一發(fā)出去線程就阻塞住等待回調(diào),需要給每個請求新建一個線程,這是很浪費的,這種方式唯一的好處應(yīng)該是易于控制請求并發(fā)的數(shù)量。
C.在子線程調(diào)異步接口
子線程調(diào)用異步接口,子線程需要有Runloop去接收異步回調(diào)事件,這里也可以每個請求都新建一條帶有Runloop的線程去偵聽回調(diào),但這一點好處都沒有,既然是異步回調(diào),除了處理回調(diào)內(nèi)容,其他時間線程都是空閑可利用的,所有請求共用一個響應(yīng)的線程就夠了。
AFNetworking用的就是第三種方式,創(chuàng)建了一條常駐線程專門處理所有請求的回調(diào)事件,這個模型跟nodejs有點類似。網(wǎng)絡(luò)請求回調(diào)處理完,組裝好數(shù)據(jù)后再給上層調(diào)用者回調(diào),這時候回調(diào)是拋回主線程的,因為主線程是最安全的,使用者可能會在回調(diào)中更新UI,在子線程更新UI會導(dǎo)致各種問題,一般使用者也可以不需要關(guān)心線程問題。
NSURLSession
絡(luò)設(shè)置:參考NSURLConnection中的設(shè)置項。
1. 創(chuàng)建一個NSURLSession,系統(tǒng)提供了兩個創(chuàng)建方法:
sessionWithConfiguration:
sessionWithConfiguration:delegate:delegateQueue:
第一個粒度較低就是根據(jù)剛才創(chuàng)建的Configuration創(chuàng)建一個Session,系統(tǒng)默認(rèn)創(chuàng)建一個新的OperationQueue處理Session的消息。
第二個粒度比較高,可以設(shè)定回調(diào)的delegate(注意這個回調(diào)delegate會被強(qiáng)引用),并且可以設(shè)定delegate在哪個OperationQueue回調(diào),如果我們將其設(shè)置為[NSOperationQueue mainQueue]就能在主線程進(jìn)行回調(diào)非常的方便。OperationQueue不傳系統(tǒng)會自動在子線程返回,AF3.x在這里傳入一個子操作隊列,讓其回調(diào)在同一個操作隊列中進(jìn)行。
2.創(chuàng)建一個NSURLRequest調(diào)用剛才的NSURLSession對象提供的Task函數(shù),創(chuàng)建一個NSURLSessionTask。
根據(jù)職能不同Task有三種子類:
NSURLSessionUploadTask:上傳用的Task,傳完以后不會再下載返回結(jié)果;
NSURLSessionDownloadTask:下載用的Task;
NSURLSessionDataTask:可以上傳內(nèi)容,上傳完成后再進(jìn)行下載。
得到的Task,調(diào)用resume開始工作,默認(rèn)是掛起的。
NSURLSession是線程安全的,在多線程方面的支持也比URLConnection要好。
NSURLSessionTask會在子線程工作,不會阻礙主線程。在AF3.0沒有再使用runloop來接受回調(diào)事件,而是創(chuàng)建NSURLSession時,傳入了一個子操作隊列,所有的回調(diào)事件都在這個操作隊列中處理。Runloop的模式不會影響它的回調(diào)。