RunLoop的作用
- 使程序一直運(yùn)行并接受用戶輸入
- 決定程序在何時應(yīng)處理哪些Event
- 解耦主調(diào)方(發(fā)起調(diào)用)與被調(diào)方(執(zhí)行線程),避免主調(diào)方被被調(diào)方阻塞。
- 節(jié)省CPU資源(閑置時就不需要CPU分配資源)
OC中的RunLoop
OC中英遭,提供了兩個RunLoop的對象:NSRunLoop 和 CFRunLoopRef榨为。
CFRunLoopRef 是在 CoreFoundation 框架內(nèi)的,它提供了純 C 函數(shù)的 API品擎,所有這些 API 都是線程安全的罕拂。
NSRunLoop 是基于 CFRunLoopRef 的封裝揍异,提供了面向?qū)ο蟮?API,但是這些 API 不是線程安全的爆班。
Swift 開源后蒿秦,蘋果又維護(hù)了一個跨平臺 CoreFoundation 版本,這個版本的源碼和現(xiàn)有 iOS 系統(tǒng)中的實(shí)現(xiàn)略不一樣蛋济,但更容易編譯,而且已經(jīng)適配了 Linux/Windows炮叶。
RunLoop 與線程的關(guān)系
- CFRunLoop 是基于 pthread 來管理的碗旅。
- 線程和 RunLoop 之間是一一對應(yīng)的,其關(guān)系是保存在一個全局的 Dictionary 里(但并不是說一個線程里只能有一個RunLoop镜悉,因?yàn)镽unLoop中是可以嵌套RunLoop的)祟辟。
- 線程剛創(chuàng)建時并沒有 RunLoop,如果你不主動獲取侣肄,那它一直都不會有旧困。
- RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時,RunLoop 的銷毀是發(fā)生在線程結(jié)束時稼锅。
- 你只能在一個線程的內(nèi)部獲取其 RunLoop(主線程除外)吼具。
RunLoop與NSTimer
NSTimer實(shí)例需要在RunLoop上進(jìn)行調(diào)度才能正常運(yùn)行,NSTimer在未加入到RunLoop前矩距,并不會產(chǎn)生效果拗盒,只有當(dāng)其加入到RunLoop時(個人猜測此時會將要執(zhí)行的事件、對應(yīng)時間點(diǎn)等橋接到RunLoop的對應(yīng)屬性中)锥债,才會在RunLoop的循環(huán)中被執(zhí)行陡蝇。
更多NSTimer相關(guān)參考:
NSTimer詳解----使用、保留環(huán)問題哮肚、與runloop的關(guān)系
重新認(rèn)識NSTimer以及他與RunLoop關(guān)系
RunLoop與其他Cocoa相關(guān)
UIEvent事件的回調(diào)是依托在主線程的RunLoop中登夫,基本上所有UI事件的起點(diǎn)都在MainRunLoop中,這也是為什么如果在主線程中有大量耗時處理時會造成App反應(yīng)遲鈍允趟。
Autorelease恼策,自動釋放池中的變量會在其RunLoop的一次循環(huán)即將結(jié)束時才將池中的對象釋放。
調(diào)用PerformSelecter方法時拼窥,會在內(nèi)部創(chuàng)建一個Timer并添加到對應(yīng)RunLoop中戏蔑。
CADisplayLink蹋凝、CATransition、CAAnimation等等與界面顯示相關(guān)的方法总棵,都與主線程的RunLoop執(zhí)行頻率有關(guān)鳍寂,RunLoop在一個循環(huán)結(jié)束前收集所有界面變化,匯總后在循環(huán)結(jié)束時調(diào)用渲染的方法進(jìn)行渲染情龄。
RunLoop的實(shí)現(xiàn)中使用了許多GCD相關(guān)代碼來進(jìn)行線程的調(diào)度工作迄汛,RunLoop與GCD只是協(xié)作關(guān)系,并不是對GCD的封裝骤视。
NSURLConnection進(jìn)行網(wǎng)絡(luò)請求時鞍爱,不論是同步還是異步,都需要用到RunLoop來等待返回值专酗。
等等...
RunLoop的流程
下圖就是一個RunLoop的流程:
關(guān)于source0和source1
- source0負(fù)責(zé)App內(nèi)部事件睹逃,由App負(fù)責(zé)管理觸發(fā),例如UIEvent祷肯、UITouch事件沉填,一般來說,App內(nèi)部的調(diào)用都屬于它的范圍佑笋。
- source1 基于port的:包含一個 mach_port 和一個回調(diào)翼闹,可監(jiān)聽系統(tǒng)端口和通過內(nèi)核和其他線程發(fā)送的消息,能主動喚醒runloop蒋纬,一般用于接收分發(fā)系統(tǒng)事件和App間的相互通信猎荠,一些硬件事件(觸摸/鎖屏/搖晃等)也是用source1來進(jìn)行接收,然后調(diào)用_UIApplicationHandleEventQueue() 進(jìn)行應(yīng)用內(nèi)部的分發(fā)(這就變成了source0)蜀备。
什么時候使用RunLoop
- 開啟一個常駐線程(讓一個子線程不進(jìn)入消亡狀態(tài),等待其他線程發(fā)來消息,處理其他事件)
- 在子線程中開啟一個定時器
- 在子線程中進(jìn)行一些長期監(jiān)控
- 控制定時器在特定模式下執(zhí)行
- 讓某些事件(行為关摇、任務(wù))在特定模式下執(zhí)行
- 添加Observer監(jiān)聽RunLoop的狀態(tài),比如監(jiān)聽點(diǎn)擊事件的處理(在所有點(diǎn)擊事件之前做一些事情)
參考文章:
關(guān)于Runloop的原理探究及基本使用
深入理解RunLoop
解密——神秘的RunLoop
NSRunLoop原理詳解——不再有盲點(diǎn)