分析IOS RunLoop的事件循環(huán)機(jī)制
在RunLoop啟動(dòng)之后會(huì)發(fā)送一個(gè)通知,來(lái)告知觀察者
將要處理Timer/Source0事件這樣一個(gè)通知的發(fā)送
處理Source0事件
如果有Source1要處理,這時(shí)會(huì)通過(guò)一個(gè)go to語(yǔ)句的實(shí)現(xiàn)來(lái)進(jìn)行代碼邏輯的跳轉(zhuǎn),處理喚醒是收到的消息
如果沒有Source1要處理,線程就將要休眠,同時(shí)發(fā)送一個(gè)通知,告訴觀察者
然后線程進(jìn)入一個(gè)用戶態(tài)到內(nèi)核態(tài)的切換,休眠,然后等待喚醒,喚醒的條件大約包括三種:
1、Source1
2、Timer事件
3、外部手動(dòng)喚醒
線程剛被喚醒之后也要發(fā)送一個(gè)通知告訴觀察者,然后處理喚醒時(shí)收到的消息
回到將要處理Timer/Source0事件這樣一個(gè)通知的發(fā)送
然后再次進(jìn)行上面步驟,這就是一個(gè)RunLoop的事件循環(huán)機(jī)制
內(nèi)部代碼邏輯整理如下:
/// 用DefaultMode啟動(dòng)
void CFRunLoopRun(void) {
CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
}
/// 用指定的Mode啟動(dòng),允許設(shè)置RunLoop超時(shí)時(shí)間
int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
/// RunLoop的實(shí)現(xiàn)
int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {
/// 首先根據(jù)modeName找到對(duì)應(yīng)mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);
/// 如果mode里沒有source/timer/observer, 直接返回。
if (__CFRunLoopModeIsEmpty(currentMode)) return;
/// 1. 通知 Observers: RunLoop 即將進(jìn)入 loop。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
/// 內(nèi)部函數(shù),進(jìn)入loop
__CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
Boolean sourceHandledThisLoop = NO;
int retVal = 0;
do {
/// 2. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
/// 執(zhí)行被加入的block
__CFRunLoopDoBlocks(runloop, currentMode);
/// 4. RunLoop 觸發(fā) Source0 (非port) 回調(diào)。
sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
/// 執(zhí)行被加入的block
__CFRunLoopDoBlocks(runloop, currentMode);
/// 5. 如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息。
if (__Source0DidDispatchPortLastTime) {
Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
if (hasMsg) goto handle_msg;
}
/// 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)。
if (!sourceHandledThisLoop) {
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
}
/// 7. 調(diào)用 mach_msg 等待接受 mach_port 的消息。線程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒。
/// • 一個(gè)基于 port 的Source 的事件。
/// • 一個(gè) Timer 到時(shí)間了
/// • RunLoop 自身的超時(shí)時(shí)間到了
/// • 被其他什么調(diào)用者手動(dòng)喚醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
}
/// 8. 通知 Observers: RunLoop 的線程剛剛被喚醒了。
__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
/// 收到消息,處理消息。
handle_msg:
/// 9.1 如果一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)。
if (msg_is_timer) {
__CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
}
/// 9.2 如果有dispatch到main_queue的block,執(zhí)行block。
else if (msg_is_dispatch) {
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}
/// 9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了,處理這個(gè)事件
else {
CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
if (sourceHandledThisLoop) {
mach_msg(reply, MACH_SEND_MSG, reply);
}
}
/// 執(zhí)行加入到Loop的block
__CFRunLoopDoBlocks(runloop, currentMode);
if (sourceHandledThisLoop && stopAfterHandle) {
/// 進(jìn)入loop時(shí)參數(shù)說(shuō)處理完事件就返回。
retVal = kCFRunLoopRunHandledSource;
} else if (timeout) {
/// 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間了
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(runloop)) {
/// 被外部調(diào)用者強(qiáng)制停止了
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
/// source/timer/observer一個(gè)都沒有了
retVal = kCFRunLoopRunFinished;
}
/// 如果沒超時(shí),mode里沒空,loop也沒被停止,那繼續(xù)loop。
} while (retVal == 0);
}
/// 10. 通知 Observers: RunLoop 即將退出。
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
}
可以看到,實(shí)際上 RunLoop 就是這樣一個(gè)函數(shù),其內(nèi)部是一個(gè)do-while循環(huán)。當(dāng)你調(diào)用CFRunLoopRun()時(shí),線程就會(huì)一直停留在這個(gè)循環(huán)里;直到超時(shí)或被手動(dòng)停止,該函數(shù)才會(huì)返回
有一個(gè)這樣的問題:當(dāng)我們點(diǎn)擊一個(gè)app,從我們點(diǎn)擊到程序啟動(dòng)、程序運(yùn)行再到程序殺死這個(gè)過(guò)程,系統(tǒng)都發(fā)生了什么呢?
實(shí)際上當(dāng)我們調(diào)用了main函數(shù)之后,會(huì)調(diào)用UIApplicationMain函數(shù),在這個(gè)函數(shù)內(nèi)部會(huì)啟動(dòng)主線程的RunLoop,然后經(jīng)過(guò)一系列的處理,最終主線程的RunLoop會(huì)處于一個(gè)休眠狀態(tài),然后我們此時(shí)如果點(diǎn)擊一下屏幕,會(huì)轉(zhuǎn)化成一個(gè)Source1來(lái)讓我們的主線程喚醒,然后當(dāng)我們殺死程序時(shí),會(huì)調(diào)用RunLoop的退出,同時(shí)發(fā)送通知告訴觀察者
找到一張總結(jié)圖幫助記憶:

以上就是分析IOS RunLoop的事件循環(huán)機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于IOS RunLoop的事件循環(huán)機(jī)制的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Observing?KVO?Key-Value基本使用原理示例詳解
這篇文章主要為大家介紹了Observing?KVO?Key-Value基本使用原理示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08
iOS如何利用一句話完成轉(zhuǎn)場(chǎng)動(dòng)畫
這篇文章主要給大家介紹了關(guān)于iOS如何利用一句話完成轉(zhuǎn)場(chǎng)動(dòng)畫的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08
iOS中UIView實(shí)現(xiàn)不同方向的導(dǎo)角
這篇文章主要給大家介紹了關(guān)于iOS中UIView實(shí)現(xiàn)不同方向的導(dǎo)角的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或使用iOS具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2018-05-05
剖析iOS開發(fā)中Cocos2d-x的內(nèi)存管理相關(guān)操作
這篇文章主要介紹了剖析iOS開發(fā)中Cocos2d-x的內(nèi)存管理相關(guān)操作,Cocos2d-x是開發(fā)游戲的利器,需要的朋友可以參考下2015-10-10
IOS 圖文混排(CoreText.framework)詳解及實(shí)例
這篇文章主要介紹了IOS 圖文混排(CoreText.framework)詳解及實(shí)例的相關(guān)資料,這里對(duì)IOS 的圖文混排進(jìn)行了詳細(xì)介紹,并附代碼實(shí)例,和實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-11-11
iOS App開發(fā)中Core Data框架基本的數(shù)據(jù)管理功能小結(jié)
除了使用SQL關(guān)系型數(shù)據(jù)庫(kù),我們還可以使用Xcode中提供的Core Data來(lái)進(jìn)行表結(jié)構(gòu)數(shù)據(jù)處理,這里我們就來(lái)初步整理iOS App開發(fā)中Core Data框架基本的數(shù)據(jù)管理功能小結(jié):2016-06-06
Swift中的HTTP請(qǐng)求體Request Bodies使用示例詳解
這篇文章主要為大家介紹了Swift中的HTTP請(qǐng)求體Request Bodies使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02
分析IOS RunLoop的事件循環(huán)機(jī)制
RunLoop是與線程相關(guān)的基礎(chǔ)架構(gòu)中的一部分,它是一個(gè)處理事件的循環(huán)(線程進(jìn)入這個(gè)循環(huán),運(yùn)行事件處理程序來(lái)響應(yīng)傳入的事件),RunLoop的目的是當(dāng)有事件需要處理時(shí),線程是活躍的、忙碌的,當(dāng)沒有事件后,線程進(jìn)入休眠。2021-06-06

