淺談我們開發(fā)生活中的那些圈

我們常常會(huì)遇到或者被問到,NSRunloop到底是什么以及有什么作用击喂?作為一枚iOS開發(fā)的老菜鳥......我是這樣理解的:

如果我們子線程需要依賴另一事務(wù)(通常是另一線程)的某個(gè)狀態(tài)维苔,為了不讓子線程一直處于while循環(huán)(親身經(jīng)歷:有while死循環(huán)時(shí)退后臺(tái)會(huì)時(shí)CPU占用增加而導(dǎo)致發(fā)熱...),又想讓線程一直運(yùn)行......

那么NSRunloop就是為了倍海活我們子線程而存在的介时,它在接受一些特定事件后能夠”喚醒“線程,處理完畢后再讓線程”休眠“凌彬,這樣就能合理的調(diào)控系統(tǒng)資源沸柔。主線程能夠接收到ui以及其他事件,也是通過rl來傳遞的铲敛,主線程的runloop是默認(rèn)開啟的褐澎,而子線程的runloop需要我們”手動(dòng)“開啟,我們來看下相關(guān)源碼:

獲取rl

我們無法直接創(chuàng)建子線程的runloop伐蒋,而是通過GetCurrent獲取當(dāng)前runloop工三,底層會(huì)判斷是否存在,如果沒有則創(chuàng)建runloop先鱼,并放入table里徒蟆。下次直接從table里返回runloop。

我們?cè)倏聪翹SRunloop的對(duì)象型型,它實(shí)際是一個(gè)cfrunloop的結(jié)構(gòu)體段审,里面有鎖、Modes、ModeItems等寺枉。

NSRunloop對(duì)象

這里再引入RunLoop主要涉及的五個(gè)類:

CFRunLoop:RunLoop對(duì)象抑淫、

CFRunLoopMode:五種RunLoop運(yùn)行模式、

CFRunLoopSource:輸入源/事件源姥闪,包括Source0?和?Source1

CFRunLoopTimer:定時(shí)源始苇,就是NSTimer、

CFRunLoopObserver:觀察者筐喳,用來監(jiān)聽RunLoop催式。


CFRunLoopMode:RunLoop運(yùn)行模式,有五種:

①kCFRunLoopDefaultMode:默認(rèn)的運(yùn)行模式避归,通常主線程是在這個(gè) Mode 下運(yùn)行的荣月。

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

③UIInitializationRunLoopMode:在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用账锹。

④GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內(nèi)部 Mode萌业,通常用不到。

⑤kCFRunLoopCommonModes:是一個(gè)偽模式奸柬,可以在標(biāo)記為CommonModes的模式下運(yùn)行生年,RunLoop會(huì)自動(dòng)將_commonModeItems里的?Source、Observer廓奕、Timer?同步到具有此標(biāo)記的Mode里抱婉。


CFRunLoopSource:輸入源/事件源,包括Source0 和 Source1兩種:

Source1:基于mach_Port懂从,處理來自系統(tǒng)內(nèi)核或其它進(jìn)程的事件授段,比如點(diǎn)擊手機(jī)屏幕蹲蒲。

Source0?:非基于Port的處理事件番甩,也就是應(yīng)用層事件,需要手動(dòng)標(biāo)記為待處理和手動(dòng)喚醒RunLoop届搁。

簡(jiǎn)單舉例:一個(gè)APP在前臺(tái)靜止缘薛,用戶點(diǎn)擊APP界面,屏幕表面的事件會(huì)先包裝成Event告訴source1(mach_port)卡睦,source1喚醒RunLoop將事件Event分發(fā)給source0宴胧,由source0來處理。


CFRunLoopTimer:定時(shí)源表锻,就是NSTimer恕齐。在預(yù)設(shè)的時(shí)間點(diǎn)喚醒RunLoop執(zhí)行回調(diào)。因?yàn)樗腔赗unLoop的瞬逊,因此它不是實(shí)時(shí)的(就是NSTimer 是不準(zhǔn)確的显歧。 因?yàn)镽unLoop只負(fù)責(zé)分發(fā)源的消息仪或。如果線程當(dāng)前正在處理繁重的任務(wù),就有可能導(dǎo)致Timer本次延時(shí)士骤,或者少執(zhí)行一次)范删。


CFRunLoopObserver:觀察者,用來監(jiān)聽以下時(shí)間點(diǎn):CFRunLoopActivity

kCFRunLoopEntry:RunLoop準(zhǔn)備啟動(dòng)

kCFRunLoopBeforeTimers:RunLoop將要處理一些Timer相關(guān)事件

kCFRunLoopBeforeSources:RunLoop將要處理一些Source事件

kCFRunLoopBeforeWaiting:RunLoop將要進(jìn)行休眠狀態(tài),即將由用戶態(tài)切換到內(nèi)核態(tài)

kCFRunLoopAfterWaiting:RunLoop被喚醒拷肌,即從內(nèi)核態(tài)切換到用戶態(tài)后

kCFRunLoopExit:RunLoop退出

kCFRunLoopAllActivities:監(jiān)聽所有狀態(tài)


從runloop結(jié)構(gòu)體可以看出到旦,runloop對(duì)象里可以有多個(gè)mode,實(shí)際上mode和source巨缘、timer添忘、observer也是一對(duì)多的關(guān)系〈铮可以理解為一個(gè)線程只能有一個(gè)runloop昔汉,一個(gè)runloop可以有多種mode來處理各種不同的事務(wù),每個(gè)mode下可以有多個(gè)source和timer拴清。


我們?cè)谧泳€程獲取到runloop對(duì)象后靶病,必須要讓它run起來,這樣才能使runloop的笨谟瑁活功能生效(當(dāng)然這是通過驗(yàn)證而得)娄周。下面我們來看下run:

run源碼

runloop在run的時(shí)候,底層實(shí)際是一個(gè)do while循環(huán)沪停,這個(gè)循環(huán)不是一直在運(yùn)行煤辨,而是通過端口集mach port set來控制循環(huán),如果不需要處理事情的時(shí)候木张,就進(jìn)行休眠狀態(tài)众辨,休眠后需要通過wakeup來喚醒,休眠時(shí)線程被block住舷礼,此時(shí)需要在另外一個(gè)線程對(duì)它進(jìn)行喚醒鹃彻。

從上圖可知,調(diào)用CFRunLoopWakeUp的時(shí)候妻献,實(shí)際是通過mach port來發(fā)送MACH_SEND_MSG的權(quán)限

循環(huán)機(jī)制內(nèi)部流程

從上圖可知蛛株,喚醒runloop有四種方式:

1、Timer事件(底層實(shí)際也是通過端口port來實(shí)現(xiàn)的)

2育拨、Source 1(也是基于端口來實(shí)現(xiàn)的)

3谨履、手動(dòng)wake up(上面提過)

4、處于超時(shí)狀態(tài)(可以手動(dòng)設(shè)置超時(shí)時(shí)間熬丧,當(dāng)超時(shí)后笋粟,會(huì)先喚醒runloop,然后再進(jìn)行退出)

以下是Runloop源碼,可以結(jié)合上面的循環(huán)機(jī)制加以理解:

第20行__CFRunLoopRun函數(shù)是核心害捕,進(jìn)入前后會(huì)通知觀察者唆香,__CFRunLoopRun內(nèi)部是一個(gè)do-while循環(huán)。

舉例:第77行__CFRunLoopDoTimers實(shí)際就會(huì)調(diào)用__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__吨艇,然后執(zhí)行timer躬它。通過查看調(diào)用棧也可以得到。

那這個(gè)Timer是如何添加進(jìn)runloop的呢东涡?

通過上面對(duì)CommonMode的了解冯吓,RunLoop會(huì)自動(dòng)將_commonModeItems里的?Source、Observer疮跑、Timer?同步到具有此標(biāo)記的CommonMode里组贺。

在執(zhí)行- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode函數(shù)后,實(shí)際就是將timer添加到相應(yīng)的mode里去祖娘,這里是[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];

添加到CommonModes中后失尖,在runloop開始run的時(shí)候,就可以通過__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__通知timer去執(zhí)行渐苏。然后通過判斷retVal的值掀潮,看runloop是否需要結(jié)束,或是讓runloop休眠琼富。至此就完成了一輪”圈“仪吧。


補(bǔ)充:CommonMode并不是一種實(shí)際的模式,和defaultmode完全不是一回事鞠眉。簡(jiǎn)單的理解就是薯鼠,如果設(shè)置了?CommonMode模式,那么runloop在切換mode的同時(shí)械蹋,也把timer的事件也帶走了出皇,所以無論切到哪種mode下,timer的事件都是可以處理的哗戈。

回答tableView滾動(dòng)timer不走問題:

1.默認(rèn)滾動(dòng)事件和timer事件都是在kCFRunloopDefaultMode下的

2.此時(shí)tableView一滾動(dòng)郊艘,mode切換到UITrackingRunloopMode中,上面說谱醇,runloop同一時(shí)間只能處理一種mode的事件暇仲,那么在DefaultMode的timer就無法響應(yīng)了步做。

3.CommonMode又是一個(gè)同步多種mode的技術(shù)方案副渴,此模式下,當(dāng)runloop到UITrackingRunloopMode的時(shí)候全度,他把timer的事件也轉(zhuǎn)移到UITrackingRunloopMode下煮剧,那么timer就能走了。


Runloop的應(yīng)用1:

點(diǎn)擊事件通過硬件(屏幕)夸進(jìn)程轉(zhuǎn)發(fā)給SpringBoard(桌面管理),再通過mach port夸進(jìn)程轉(zhuǎn)發(fā)給當(dāng)前app勉盅,app再通過Source1和Source0回調(diào)佑颇,最后傳給app的UIWindow。

Runloop的應(yīng)用2:

檢測(cè)主線程卡頓

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末草娜,一起剝皮案震驚了整個(gè)濱河市挑胸,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宰闰,老刑警劉巖茬贵,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異移袍,居然都是意外死亡解藻,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門葡盗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來螟左,“玉大人,你說我怎么就攤上這事觅够〗罕常” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵喘先,是天一觀的道長(zhǎng)奄妨。 經(jīng)常有香客問我,道長(zhǎng)苹祟,這世上最難降的妖魔是什么砸抛? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮树枫,結(jié)果婚禮上直焙,老公的妹妹穿的比我還像新娘。我一直安慰自己砂轻,他們只是感情好奔誓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著搔涝,像睡著了一般厨喂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上庄呈,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天蜕煌,我揣著相機(jī)與錄音,去河邊找鬼诬留。 笑死斜纪,一個(gè)胖子當(dāng)著我的面吹牛贫母,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播盒刚,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼腺劣,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了因块?” 一聲冷哼從身側(cè)響起橘原,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涡上,沒想到半個(gè)月后靠柑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡吓懈,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年歼冰,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耻警。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡隔嫡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出甘穿,到底是詐尸還是另有隱情腮恩,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布温兼,位于F島的核電站秸滴,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏募判。R本人自食惡果不足惜荡含,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望届垫。 院中可真熱鬧释液,春花似錦、人聲如沸装处。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)妄迁。三九已至寝蹈,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間登淘,已是汗流浹背箫老。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留形帮,地道東北人槽惫。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像辩撑,于是被迫代替她去往敵國(guó)和親界斜。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 閱讀本篇文章需要有一定的runloop基礎(chǔ)、runloop的基礎(chǔ)認(rèn)知還請(qǐng)先自行搜索 RunLoop運(yùn)行流程 代碼運(yùn)...
    Avery_AN閱讀 545評(píng)論 0 0
  • RunLoop簡(jiǎn)介 運(yùn)行循環(huán)君躺,在程序運(yùn)行過程中循環(huán)做一些事情峭判,如果沒有Runloop程序執(zhí)行完畢就會(huì)立即退出,如果...
    蘋果我咬了一口閱讀 546評(píng)論 0 0
  • 之前一直對(duì)ios的RunLoop機(jī)制一知半解棕叫,很多地方不是很清楚于是每次想到這個(gè)問題都會(huì)糾結(jié)林螃,想搞明白這里邊到底做...
    簫聲亂閱讀 677評(píng)論 0 4
  • iOS 中RunLoop 是一個(gè)事件循環(huán)對(duì)象 runloop跑一圈,只能執(zhí)行一個(gè)事件俺泣。 一般一個(gè)線程執(zhí)行任務(wù)完成后...
    小李不木閱讀 605評(píng)論 0 0
  • 黑色的海島上懸著一輪又大又圓的明月伏钠,毫不嫌棄地把溫柔的月色照在這寸草不生的小島上横漏。一個(gè)少年白衣白發(fā),悠閑自如地倚坐...
    小水Vivian閱讀 3,108評(píng)論 1 5