RunLoop深入理解(一)理論

RunLoop

從看蘋果文檔灾前,了解runloop就看到是這個(gè)圖:


runloop接受輸入源顯示圖

這個(gè)圖只是說runloop利用內(nèi)核mach的通信示意圖 容握,其只是告訴我們r(jià)unloop接受消息刷喜,但是消息到底是什么(mach port消息體)妥泉,處理后怎么發(fā)哪尼吸奴,更不涉及runLoop內(nèi)部的東西共虑,所以不要被此圖迷惑了愧怜。此貌似只能初步直觀的告訴我們r(jià)unloop是一個(gè)循環(huán)。

既然說到這里妈拌,我們在這腦補(bǔ)點(diǎn)這方面的東西拥坛,傳進(jìn)出runLoop處理的完整過程蓬蝶。

首先我們先看IOS/OSX系統(tǒng)的系統(tǒng)架構(gòu)


系統(tǒng)架構(gòu)圖

劃分為4個(gè)層次:

1.應(yīng)用層包括用戶能接觸到的圖形應(yīng)用,例如 Spotlight猜惋、Aqua丸氛、SpringBoard 等。

2.應(yīng)用框架層即開發(fā)人員接觸到的 Cocoa 等框架著摔。

3.核心框架層包括各種核心框架缓窜、OpenGL 等內(nèi)容。

4.Darwin 即操作系統(tǒng)的核心谍咆,包括系統(tǒng)內(nèi)核禾锤、驅(qū)動(dòng)、Shell 等內(nèi)容摹察,這一層是開源的时肿,其所有源碼都可以在opensource.apple.com里找到。


在Darwin這個(gè)核心的架構(gòu):

硬件層上面的三個(gè)組成部分:Mach港粱、BSD螃成、IOKit (還包括一些上面沒標(biāo)注的內(nèi)容),共同組成了 XNU 內(nèi)核

BSD 層可以看作圍繞 Mach 層的一個(gè)外環(huán)查坪,其提供了諸如進(jìn)程管理寸宏、文件系統(tǒng)和網(wǎng)絡(luò)等功能。

Mach 本身提供的 API 非常有限偿曙,而且蘋果也不鼓勵(lì)使用 Mach 的 API氮凝,但是這些API非常基礎(chǔ)望忆,如果沒有這些API的話罩阵,其他任何工作都無法實(shí)施。在 Mach 中启摄,所有的東西都是通過自己的對象實(shí)現(xiàn)的稿壁,進(jìn)程、線程和虛擬內(nèi)存都被稱為"對象"歉备。和其他架構(gòu)不同傅是, Mach 的對象間不能直接調(diào)用,只能通過消息傳遞的方式實(shí)現(xiàn)對象間的通信蕾羊。"消息"是 Mach 中最基礎(chǔ)的概念喧笔,消息在兩個(gè)端口 (port) 之間傳遞,這就是 Mach 的 IPC (進(jìn)程間通信) 的核心龟再。

mach發(fā)送和接受消息是通過同一個(gè) mach_msg() 進(jìn)行的书闸。

說這么多mach是什么意思尼?

RunLoop 的核心就是一個(gè) mach_msg() (文章尾部的runloop源代碼利凑,來說明此點(diǎn))浆劲,RunLoop 調(diào)用這個(gè)函數(shù)去接收消息嫌术,如果沒有別人發(fā)送 port 消息過來,內(nèi)核會將線程置于等待狀態(tài)梳侨。例如你在模擬器里跑起一個(gè) iOS 的 App蛉威,然后在 App 靜止時(shí)點(diǎn)擊暫停日丹,你會看到主線程調(diào)用棧是停留在 mach_msg_trap() 這個(gè)地方走哺。(至于Port消息結(jié)構(gòu)體什么樣,請查找源代碼)

俯瞰后runloop后哲虾,咱們平視r(shí)unloop內(nèi)部:

在 CoreFoundation 里面關(guān)于 RunLoop 有5個(gè)類:

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimerRef

CFRunLoopObserverRef

這里特別說下CFRunLoopModeRef類 這個(gè)類并沒有對外暴漏丙躏,在runloop源代碼里,通過 CFRunLoopRef 的接口進(jìn)行了封裝束凑,如圖:

runloop封裝CFRunLoopModeRef

一個(gè)RunLoop包含有N個(gè)Mode 每個(gè)Mode又包含N個(gè)Source/Timer/Observer(比如你在主線程runloop可以建立無數(shù)個(gè)timer晒旅,observer,以及系統(tǒng)自己無數(shù)輸入源點(diǎn))汪诉。

每次調(diào)用 RunLoop 的主函數(shù)時(shí)废恋,只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode扒寄。如果需要切換 Mode鱼鼓,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入该编。這樣做主要是為了分隔開不同組的 Source/Timer/Observer迄本,讓其互不影響。(這句話對實(shí)戰(zhàn)可是有意義的课竣,當(dāng)scroolview豎直滑動(dòng)過程嘉赎,如和保證其上有timer橫行滾動(dòng)焦點(diǎn)圖尼?)

至于CFRunLoopSourceRef于樟,CFRunLoopTimerRef公条,CFRunLoopObserverRef,這個(gè)不用多說了吧迂曲。

Source/Timer/Observer 被統(tǒng)稱為 mode item赃份,一個(gè) item 可以被同時(shí)加入多個(gè) mode。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會有效果的奢米。如果一個(gè) mode 中一個(gè) item 都沒有抓韩,則 RunLoop 會直接退出,不進(jìn)入循環(huán)鬓长。

一個(gè) Mode 可以將自己標(biāo)記為"Common"屬性(通過將其 ModeName 添加到 RunLoop 的 "commonModes" 中)谒拴。每當(dāng) RunLoop 的內(nèi)容發(fā)生變化時(shí),RunLoop 都會自動(dòng)將 _commonModeItems 里的 Source/Observer/Timer 同步到具有 "Common" 標(biāo)記的所有Mode里涉波。

應(yīng)用場景舉例:主線程的 RunLoop 里有兩個(gè)預(yù)置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode英上。這兩個(gè) Mode 都已經(jīng)被標(biāo)記為"Common"屬性炭序。DefaultMode 是 App 平時(shí)所處的狀態(tài),TrackingRunLoopMode 是追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)苍日。當(dāng)你創(chuàng)建一個(gè) Timer 并加到 DefaultMode 時(shí)惭聂,Timer 會得到重復(fù)回調(diào),但此時(shí)滑動(dòng)一個(gè)TableView時(shí)相恃,RunLoop 會將 mode 切換為 TrackingRunLoopMode辜纲,這時(shí) Timer 就不會被回調(diào),并且也不會影響到滑動(dòng)操作拦耐。

RunLoop的內(nèi)部邏輯

這張圖可比第一張圖可能才是我們深入runloop的第一步耕腾,咱們分析下

1.App 啟動(dòng)后 RunLoop 的狀態(tài):

系統(tǒng)默認(rèn)注冊了5個(gè)Mode:

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

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

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

4: GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode骂际,通常用不到疗琉。

5: kCFRunLoopCommonModes: 這是一個(gè)占位的 Mode,沒有實(shí)際作用方援。

你可以在這里看到更多的蘋果內(nèi)部的 Mode没炒,但那些 Mode 在開發(fā)中就很難遇到了。

當(dāng) RunLoop 進(jìn)行回調(diào)時(shí)犯戏,一般都是通過一個(gè)很長的函數(shù)調(diào)用出去 (call out), 當(dāng)你在你的代碼中下斷點(diǎn)調(diào)試時(shí)送火,通常能在調(diào)用棧上看到這些函數(shù)

1.static?void?__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__();//observe

2.static?void?__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__();//block

3.static?void?__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__();//main_dispatch

4.static?void?__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__();//timer

5.static?void?__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__();//source0事件

6.static?void?__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__();//source1事件

1.Observer事件,runloop中狀態(tài)變化時(shí)進(jìn)行通知先匪。(微信卡頓監(jiān)控就是利用這個(gè)事件通知來記錄下最近一次main runloop活動(dòng)時(shí)間种吸,在另一個(gè)check線程中用定時(shí)器檢測當(dāng)前時(shí)間距離最后一次活動(dòng)時(shí)間過久來判斷在主線程中的處理邏輯耗時(shí)和卡主線程)。這里還需要特別注意呀非,CAAnimation是由RunloopObserver觸發(fā)回調(diào)來重繪坚俗,接下來會講到。

2.Block事件岸裙,非延遲的NSObject PerformSelector立即調(diào)用猖败,dispatch_after立即調(diào)用,block回調(diào)降允。

3.Main_Dispatch_Queue事件:GCD中dispatch到main queue的block會被dispatch到main loop執(zhí)行恩闻。

4.Timer事件:延遲的NSObject PerformSelector,延遲的dispatch_after剧董,timer事件幢尚。

5.Source0事件:處理如UIEvent破停,CFSocket這類事件。需要手動(dòng)觸發(fā)尉剩。觸摸事件其實(shí)是Source1接收系統(tǒng)事件后在回調(diào) __IOHIDEventSystemClientQueueCallback() 內(nèi)觸發(fā)的 Source0真慢,Source0 再觸發(fā)的 _UIApplicationHandleEventQueue()。source0一定是要喚醒runloop及時(shí)響應(yīng)并執(zhí)行的理茎,如果runloop此時(shí)在休眠等待系統(tǒng)的 mach_msg事件黑界,那么就會通過source1來喚醒runloop執(zhí)行。

6.Source1事件:處理系統(tǒng)內(nèi)核的mach_msg事件功蜓。(推測CADisplayLink也是這里觸發(fā))园爷。

關(guān)于以前的框架 你會發(fā)現(xiàn)main函數(shù)里有@autoreleasePool{}

AutoreleasePool

App啟動(dòng)后宠蚂,蘋果在主線程 RunLoop 里注冊了兩個(gè) Observer式撼,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。

第一個(gè) Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop)求厕,其回調(diào)內(nèi)會調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池著隆。其 order 是-2147483647,優(yōu)先級最高呀癣,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前美浦。

第二個(gè) Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來釋放自動(dòng)釋放池项栏。這個(gè) Observer 的 order 是 2147483647浦辨,優(yōu)先級最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后沼沈。

在主線程執(zhí)行的代碼流酬,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的列另。這些回調(diào)會被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著芽腾,所以不會出現(xiàn)內(nèi)存泄漏,開發(fā)者也不必顯示創(chuàng)建 Pool 了页衙。



綜合多篇文章整理所有

參考:深入理解RunLoop

蘋果runLoop文檔

蘋果文檔中文翻譯版懶人版


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末摊滔,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子店乐,更是在濱河造成了極大的恐慌艰躺,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眨八,死亡現(xiàn)場離奇詭異腺兴,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)踪古,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門含长,熙熙樓的掌柜王于貴愁眉苦臉地迎上來券腔,“玉大人,你說我怎么就攤上這事拘泞》兹遥” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵陪腌,是天一觀的道長辱魁。 經(jīng)常有香客問我,道長诗鸭,這世上最難降的妖魔是什么染簇? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮强岸,結(jié)果婚禮上锻弓,老公的妹妹穿的比我還像新娘。我一直安慰自己蝌箍,他們只是感情好青灼,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著妓盲,像睡著了一般杂拨。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上悯衬,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天弹沽,我揣著相機(jī)與錄音,去河邊找鬼筋粗。 笑死策橘,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的亏狰。 我是一名探鬼主播役纹,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼暇唾!你這毒婦竟也來了促脉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤策州,失蹤者是張志新(化名)和其女友劉穎瘸味,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體够挂,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡旁仿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片枯冈。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡毅贮,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出尘奏,到底是詐尸還是另有隱情滩褥,我是刑警寧澤,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布炫加,位于F島的核電站瑰煎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏俗孝。R本人自食惡果不足惜酒甸,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望赋铝。 院中可真熱鬧插勤,春花似錦、人聲如沸柬甥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苛蒲。三九已至,卻和暖如春绿满,著一層夾襖步出監(jiān)牢的瞬間臂外,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工喇颁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留漏健,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓橘霎,卻偏偏與公主長得像蔫浆,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子姐叁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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