RunLoop是什么旨剥?
RunLoop
是通過內(nèi)部維護的 事件循環(huán)(Event Loop
) 來對 事件/消息 進行管理的一個對象机久。
事件循環(huán)空扎,絕對不止是死循環(huán)這么簡單的一個回答。實質(zhì)上就是runloop內(nèi)部狀態(tài)的轉(zhuǎn)換润讥。
沒有消息處理時转锈,休眠已避免資源占用,由用戶態(tài)切換到內(nèi)核態(tài)(CPU-內(nèi)核態(tài)和用戶態(tài))
-內(nèi)核態(tài):系統(tǒng)調(diào)用楚殿,牽涉到操作系統(tǒng)撮慨,底層內(nèi)核相關(guān)的指令。有消息需要處理時脆粥,立刻被喚醒砌溺,由內(nèi)核態(tài)切換到用戶態(tài)
-用戶態(tài):應(yīng)用程序都是在用戶態(tài),平時開發(fā)用到的api等都是用戶態(tài)的操作
RunLoop
實際上是一個對象变隔,這個對象在循環(huán)中用來處理程序運行過程中出現(xiàn)的各種事件(比如說觸摸事件规伐、UI
刷新事件、定時器事件匣缘、Selector
事件)猖闪,從而保持程序的持續(xù)運行。RunLoop
在沒有事件處理的時候肌厨,會使線程進入睡眠模式培慌,從而節(jié)省 CPU
資源,提高程序性能柑爸。
Runloop與線程關(guān)系
- 線程和RunLoop是一一對應(yīng)的,其映射關(guān)系是保存在一個全局的
Dictionary
里 - 主線程的
RunLoop
對象系統(tǒng)自動幫助我們創(chuàng)建好了吵护,而子線程的RunLoop
對象需要我們主動創(chuàng)建和維護。
我們在啟動一個 iOS
程序的時候表鳍,系統(tǒng)會調(diào)用創(chuàng)建項目時自動生成的 main.m
的文件馅而。main.m
文件如下所示:
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
其中 UIApplicationMain
函數(shù)內(nèi)部幫我們開啟了主線程的 RunLoop
,UIApplicationMain
內(nèi)部擁有一個無限循環(huán)的代碼进胯,只要程序不退出/崩潰用爪,它就一直循環(huán)。上邊的代碼中主線程開啟 RunLoop
的過程可以簡單的理解為如下代碼:
int main(int argc, char * argv[]) {
BOOL running = YES;
do {
// 執(zhí)行各種任務(wù)胁镐,處理各種事件
// ......
} while (running); // 判斷是否需要退出
return 0;
}
從上邊可看出偎血,程序一直在 do-while
循環(huán)中執(zhí)行诸衔,所以 UIApplicationMain
函數(shù)一直沒有返回,我們在運行程序之后程序不會馬上退出颇玷,會保持持續(xù)運行狀態(tài)笨农。
下圖是蘋果官方給出的 RunLoop
模型圖。
從上圖中可以看出帖渠,RunLoop
就是線程中的一個循環(huán)谒亦,RunLoop
會在循環(huán)中會不斷檢測,通過 Input sources
(輸入源)和 Timer sources
(定時源)兩種來源等待接受事件空郊;然后對接受到的事件通知線程進行處理份招,并在沒有事件的時候讓線程進行休息。
RunLoop原理
我們接下來看看Runloop是怎么執(zhí)行循環(huán)的呢狞甚?
這張圖對于我們理解RunLoop來說太有幫助了,下邊我們可以來說下官方文檔給我們的RunLoop邏輯哼审。
在每次運行開啟RunLoop的時候,所在線程的RunLoop會自動處理之前未處理的事件十气,并且通知相關(guān)的觀察者。
具體的順序如下:
- 通知觀察者RunLoop已經(jīng)啟動
- 通知觀察者即將要開始的定時器
- 通知觀察者任何即將啟動的非基于端口的源
- 啟動任何準備好的非基于端口的源
- 如果基于端口的源準備好并處于等待狀態(tài)砸西,立即啟動籍胯;并進入步驟9
- 通知觀察者線程進入休眠狀態(tài)
- 將線程置于休眠知道任一下面的事件發(fā)生:
- 某一事件到達基于端口的源
- 定時器啟動
- RunLoop設(shè)置的時間已經(jīng)超時
- RunLoop被顯示喚醒
- 通知觀察者線程將被喚醒
- 處理未處理的事件
- 如果用戶定義的定時器啟動离福,處理定時器事件并重啟RunLoop。進入步驟2
- 如果輸入源啟動妖爷,傳遞相應(yīng)的消息
- 如果RunLoop被顯示喚醒而且時間還沒超時,重啟RunLoop绿聘。進入步驟2
- 通知觀察者RunLoop結(jié)束次舌。