源碼解讀RunLoop,理解以后面試必加分

繼上一篇博客

中高級(jí)iOS必備知識(shí)點(diǎn)之RunLoop(一)繼續(xù)介紹

RunLoop的狀態(tài)

首先我們?nèi)unLoop的源碼去查看它有幾種狀態(tài),如下圖:

它一共有上面的這幾種個(gè)狀態(tài)

/* Run Loop Observer Activities */

typedef?CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {

kCFRunLoopEntry = (1UL << 0), //即將進(jìn)入loop

kCFRunLoopBeforeTimers = (1UL << 1), //即將處理timer

kCFRunLoopBeforeSources = (1UL << 2), //即將處理source

kCFRunLoopBeforeWaiting = (1UL << 5), //即將進(jìn)入休眠

kCFRunLoopAfterWaiting = (1UL << 6), //剛從休眠中喚醒

kCFRunLoopExit = (1UL << 7), //即將退出loop

kCFRunLoopAllActivities = 0x0FFFFFFFU //所有模式

};

現(xiàn)在我們來(lái)試一試,怎么去監(jiān)聽(tīng)RunLoop的狀態(tài),是這樣,RunLoop的監(jiān)聽(tīng)模式?jīng)]有OC的代碼,我們可以用C語(yǔ)言代碼來(lái)實(shí)現(xiàn),如下:

從執(zhí)行的結(jié)果來(lái)看,確實(shí)是這么多,點(diǎn)擊事件是在source0執(zhí)行,所以看log日志也是很清楚,

接下來(lái)我們看一下定時(shí)器喚醒RunLoop.我們知道kCFRunLoopBeforeWaiting是休眠睡覺(jué),而kCFRunLoopAfterWaiting是喚醒休眠,我們看一下定時(shí)器是不是喚醒休眠,請(qǐng)看下面的代碼:

從輸出結(jié)果來(lái)看,確實(shí)是喚醒休眠.執(zhí)行block

證明模式切換會(huì)退出RunLoop,再重新進(jìn)入RunLoop

由上面的監(jiān)聽(tīng),我們是很容易可以證明這個(gè)結(jié)果吧?我們看一下代碼,這次用上面說(shuō)的另一種創(chuàng)建observe的方法,請(qǐng)看下圖:隨便創(chuàng)建一個(gè)可以滾動(dòng)的view,比如我創(chuàng)建的Scrollerview.

當(dāng)在滾動(dòng)的時(shí)候,我們明顯可以看到2種模式在切換,而且RunLoop也是需要退出重新進(jìn)入才會(huì)切換到新的模式.

深入理解RunLoop的執(zhí)行流程

網(wǎng)上的答案很多,這次我們從源碼解析,一步一步的查看RunLoop的執(zhí)行流程到底是怎么樣的:因?yàn)樵创a比較抽象,是純c語(yǔ)言的,不像我們之前的有c++源碼比較好懂一點(diǎn),那我們?cè)趺凑襯unloop開(kāi)始的函數(shù)呢?很容易,我們知道點(diǎn)擊也是通過(guò)runloop來(lái)處理的,那我們直接看點(diǎn)擊事件的函數(shù)調(diào)用棧就知道入口了,請(qǐng)看下圖:

從上面的代碼可以很清楚的看出來(lái)是調(diào)用了CFRunLoopRunSpecific這個(gè)函數(shù).那我們就去源碼搜索這段代碼很容易就搜索到.請(qǐng)看下面:

一看上面的源碼,我們發(fā)現(xiàn)執(zhí)行了非常多,你看綠色里面有鎖,有多線程等等,所以我們沒(méi)有必要研究得很透徹,浪費(fèi)時(shí)間也沒(méi)有意義,我們只要把大致流程捋順了就行了,所以我們只要看關(guān)鍵代碼,我只展示我們要看的關(guān)鍵代碼如下:

接下來(lái)我們就去看__CFRunLoopRun源碼里面到底執(zhí)行了哪些操作,接下來(lái)的源碼是我精簡(jiǎn)了的,大家可以參照源碼看一下,因?yàn)槔锩鏂|西非常多,我們沒(méi)有必要全部了解,只要知道它的執(zhí)行流程即可.請(qǐng)看下圖:

如果條件不成立,就會(huì)設(shè)置返回值,到時(shí)候直接退出RunLoop,上面的流程相信我備注的已經(jīng)非常清楚,對(duì)照一下源碼看一下,你會(huì)印象更加深刻.下面我們用文字總結(jié)一下流程如下:

再放一個(gè)文字版的:

這個(gè)和源碼基本一模一樣的流程,可以參照一下,源碼還是比較抽象的

接下來(lái)再看一個(gè)知識(shí)點(diǎn).

__CFRunLoopDoBlocks、__CFRunLoopDoObservers、__CFRunLoopDoTimers里面執(zhí)行了什么

其中RunLoop做了這么幾件大事中比如blocks,timers,observers,我們看下里面具體是執(zhí)行了什么,繼續(xù)看源碼,請(qǐng)看下圖,比如我們就看__CFRunLoopDoSources0:

其實(shí)最終處理的就是這個(gè):

__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__

我們?cè)倏聪轮暗囊粋€(gè)截圖

它上面兩個(gè)處理的函數(shù)是一摸一樣,也進(jìn)一步的證明了,觸摸事件是source0在處理.

定時(shí)器也是一樣的道理,大家可以自己嘗試一下.

理解線程休眠具體是什么意思?

RunLoop的線程休眠是真的休眠,它是不會(huì)占用任何cpu的資源,完全休息.它和white(1)這種還是有本質(zhì)的區(qū)別,white(1)它是一直在執(zhí)行,轉(zhuǎn)成匯編會(huì)有幾條指令,一直在執(zhí)行,一直占用cpu資源,比如我們想做優(yōu)化,那RunLoop的這種休眠模式是不是更節(jié)約cpu資源,說(shuō)白了更省電些.就是如下源碼的位置:

就是執(zhí)行到當(dāng)前代碼,就不會(huì)往下走了,不會(huì)接著執(zhí)行下面的代碼,就會(huì)堵在這里,一旦別人喚醒它了,它才會(huì)接著往下執(zhí)行.我們可能奇怪它是怎么做到的這種休眠?

我們看一下里面具體是怎么實(shí)現(xiàn)的,其實(shí)里面是執(zhí)行了非常內(nèi)核的函數(shù)叫做:mach_msg,我們可以看下

其實(shí)IPA可以分為內(nèi)核層面的IPA:它是非常非常底層的,是操作系統(tǒng)層面的,它可以讓線程休眠,也是真的休眠,不占用任何cpu資源,一般是不開(kāi)放給我們程序員使用的,因?yàn)槭潜容^內(nèi)核的,給程序員使用危險(xiǎn)也比較大,而應(yīng)用層面的IPA,都是網(wǎng)絡(luò)請(qǐng)求什么,頁(yè)面什么.

所以mach_msg,我們面試的時(shí)候可以答出這個(gè)函數(shù).

RunLoop休眠實(shí)現(xiàn)的原理

就是用戶態(tài)和內(nèi)核態(tài)的切換,用戶態(tài)發(fā)消息,內(nèi)核態(tài)休眠,再被喚醒,用戶態(tài)處理消息.

所以如果面試官問(wèn):RunLoop里面線程阻塞是怎么樣的?我們千萬(wàn)不能答,里面是個(gè)死循環(huán).就是上面剛剛說(shuō)的那些.

RunLoop與NSTimer的故事

相信我們?cè)陂_(kāi)發(fā)中遇到的次數(shù)是非常的多,這里稍微提一下.

比如我們常見(jiàn)的mode模式是有2種

1.KCFRunLoopDefaultMode (NSDefaultRunLoopMode):App的默認(rèn)Mode,通常是主線程是在這個(gè)Mode下運(yùn)行

2.UITrackingRunLoopMode : 界面跟蹤Mode,用于ScrollView追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他Mode影響

我們?cè)诎裈imer添加到runloop的時(shí)候,直接傳入通用模式即可NSRunLoopCommonModes即可.

主要說(shuō)一下它是怎么個(gè)原因.

我們看一下上個(gè)博客說(shuō)的RunLoop的結(jié)構(gòu):

我們可以理解為傳入NSRunLoopCommonModes,就是把這2種模式,放入_commonModes里面,也就是timer可以_commonModes數(shù)組中的模式下進(jìn)行工作.注意NSRunLoopCommonModes這個(gè)不是一種模式哈.

而_commonModeItems里面存放的都是可以在_commonModes模式下工作的,比如剛剛的timer.

接下來(lái)我會(huì)繼續(xù)介紹'線程?;?知識(shí)點(diǎn).

如果覺(jué)得我寫(xiě)得對(duì)您有所幫助,請(qǐng)關(guān)注我,我會(huì)持續(xù)更新??

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容