分析IOS RunLoop的事件循環(huán)機制
在RunLoop啟動之后會發(fā)送一個通知,來告知觀察者
將要處理Timer/Source0事件這樣一個通知的發(fā)送
處理Source0事件
如果有Source1要處理,這時會通過一個go to語句的實現(xiàn)來進行代碼邏輯的跳轉(zhuǎn),處理喚醒是收到的消息
如果沒有Source1要處理,線程就將要休眠,同時發(fā)送一個通知,告訴觀察者
然后線程進入一個用戶態(tài)到內(nèi)核態(tài)的切換,休眠,然后等待喚醒,喚醒的條件大約包括三種:
1、Source1
2、Timer事件
3、外部手動喚醒
線程剛被喚醒之后也要發(fā)送一個通知告訴觀察者,然后處理喚醒時收到的消息
回到將要處理Timer/Source0事件這樣一個通知的發(fā)送
然后再次進行上面步驟,這就是一個RunLoop的事件循環(huán)機制
內(nèi)部代碼邏輯整理如下:
/// 用DefaultMode啟動void CFRunLoopRun(void) { CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);} /// 用指定的Mode啟動,允許設(shè)置RunLoop超時時間int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) { return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);} /// RunLoop的實現(xiàn)int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {/// 首先根據(jù)modeName找到對應(yīng)mode CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false); /// 如果mode里沒有source/timer/observer, 直接返回。 if (__CFRunLoopModeIsEmpty(currentMode)) return;/// 1. 通知 Observers: RunLoop 即將進入 loop。 __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);/// 內(nèi)部函數(shù),進入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),直接處理這個 Source1 然后跳轉(zhuǎn)去處理消息。 if (__Source0DidDispatchPortLastTime) {Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)if (hasMsg) goto handle_msg; }/// 通知 Observers: RunLoop 的線程即將進入休眠(sleep)。 if (!sourceHandledThisLoop) {__CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting); }/// 7. 調(diào)用 mach_msg 等待接受 mach_port 的消息。線程將進入休眠, 直到被下面某一個事件喚醒。 /// • 一個基于 port 的Source 的事件。 /// • 一個 Timer 到時間了 /// • RunLoop 自身的超時時間到了 /// • 被其他什么調(diào)用者手動喚醒 __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 如果一個 Timer 到時間了,觸發(fā)這個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 如果一個 Source1 (基于port) 發(fā)出事件了,處理這個事件 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) {/// 進入loop時參數(shù)說處理完事件就返回。retVal = kCFRunLoopRunHandledSource; } else if (timeout) {/// 超出傳入?yún)?shù)標記的超時時間了retVal = kCFRunLoopRunTimedOut; } else if (__CFRunLoopIsStopped(runloop)) {/// 被外部調(diào)用者強制停止了retVal = kCFRunLoopRunStopped; } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {/// source/timer/observer一個都沒有了retVal = kCFRunLoopRunFinished; }/// 如果沒超時,mode里沒空,loop也沒被停止,那繼續(xù)loop。} while (retVal == 0); }/// 10. 通知 Observers: RunLoop 即將退出。 __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);}
可以看到,實際上 RunLoop 就是這樣一個函數(shù),其內(nèi)部是一個do-while循環(huán)。當你調(diào)用CFRunLoopRun()時,線程就會一直停留在這個循環(huán)里;直到超時或被手動停止,該函數(shù)才會返回
有一個這樣的問題:當我們點擊一個app,從我們點擊到程序啟動、程序運行再到程序殺死這個過程,系統(tǒng)都發(fā)生了什么呢?
實際上當我們調(diào)用了main函數(shù)之后,會調(diào)用UIApplicationMain函數(shù),在這個函數(shù)內(nèi)部會啟動主線程的RunLoop,然后經(jīng)過一系列的處理,最終主線程的RunLoop會處于一個休眠狀態(tài),然后我們此時如果點擊一下屏幕,會轉(zhuǎn)化成一個Source1來讓我們的主線程喚醒,然后當我們殺死程序時,會調(diào)用RunLoop的退出,同時發(fā)送通知告訴觀察者
找到一張總結(jié)圖幫助記憶:
以上就是分析IOS RunLoop的事件循環(huán)機制的詳細內(nèi)容,更多關(guān)于IOS RunLoop的事件循環(huán)機制的資料請關(guān)注好吧啦網(wǎng)其它相關(guān)文章!
相關(guān)文章:
1. ASP 信息提示函數(shù)并作返回或者轉(zhuǎn)向2. .NET SkiaSharp 生成二維碼驗證碼及指定區(qū)域截取方法實現(xiàn)3. jsp網(wǎng)頁實現(xiàn)貪吃蛇小游戲4. CentOS郵件服務(wù)器搭建系列—— POP / IMAP 服務(wù)器的構(gòu)建( Dovecot )5. Java中equals()知識點總結(jié)6. css代碼優(yōu)化的12個技巧7. ASP中if語句、select 、while循環(huán)的使用方法8. MyBatis JdbcType 與Oracle、MySql數(shù)據(jù)類型對應(yīng)關(guān)系說明9. 小技巧處理div內(nèi)容溢出10. 將properties文件的配置設(shè)置為整個Web應(yīng)用的全局變量實現(xiàn)方法
