蘋果開發(fā)文檔閱讀筆記之Runloop

本文參照蘋果官方文檔,力圖以不同角度、不同方式來(lái)描述Runloop及其相關(guān)特性,以便更立體更全面地理解Runloop。

Runloop是一種消息處理模型,或者說(shuō)消息處理循環(huán)Runloop的作用就是讓應(yīng)用程序在線程空閑時(shí)進(jìn)行休眠,而在接收到消息時(shí)再進(jìn)行相應(yīng)的處理。這樣盡可能地減少不必要的CPU占用時(shí)間,優(yōu)化資源配置。

Runloop和線程是一一對(duì)應(yīng)的,具體說(shuō)來(lái)就是一個(gè)線程對(duì)應(yīng)有一個(gè)Runloop,它們?cè)诘讓邮鞘褂米值?code>Dictionary的鍵值對(duì)來(lái)保存的。iOS應(yīng)用程序的主線程,以及NSTimer的相關(guān)API已經(jīng)自動(dòng)啟用了其對(duì)應(yīng)的Runloop,不需要人為創(chuàng)建。但如果希望保持人為創(chuàng)建的子線程的生命周期,則需要顯式的創(chuàng)建獲取其對(duì)應(yīng)的Runloop并做一些相關(guān)配置。

Runloop就是進(jìn)入線程后負(fù)責(zé)監(jiān)聽事件并讓對(duì)應(yīng)事件處理模塊進(jìn)行回調(diào)的循環(huán)體。Runloop從兩種事件源監(jiān)聽并接收事件:一個(gè)是Input Sources,一個(gè)是Timer Sources。值得注意的是,Input Sources傳遞的是異步事件,這些事件一般是從其他應(yīng)用程序的線程中傳遞過(guò)來(lái)的消息;而Timer Sources傳遞的都是同步事件,這些事件都會(huì)在特定的時(shí)間點(diǎn)或者以重復(fù)的時(shí)間間隔發(fā)生。

Runloop在處理輸入事件的同時(shí),還會(huì)生成一些描述自身當(dāng)前狀態(tài)的通知Notification。系統(tǒng)使用了一些觀察者Observer來(lái)注冊(cè)觀察這些通知,因此觀察者會(huì)在Runloop對(duì)應(yīng)的狀態(tài)改變時(shí)收到通知,然后在當(dāng)前線程做相應(yīng)的處理。

有一個(gè)很重要的概念就是Runloop Modes。Runloop同一時(shí)間只會(huì)工作在一種Mode下,然后切換到其他Mode中。一個(gè)Mode就是一個(gè)集合,里面包括一組Input Sources、一組Runloop將會(huì)監(jiān)聽的Timer Sources以及一組注冊(cè)到當(dāng)前Runloop的Observers。不同Mode可以有不同的input sources、timer sources及observers的集合,也可以存在交集。

當(dāng)你創(chuàng)建并啟動(dòng)一個(gè)Runloop時(shí),你需要指定一個(gè)Mode給這個(gè)Runloop,這樣這個(gè)Runloop就只會(huì)監(jiān)聽此Mode集合中的Timer Sources、只接收處理此Mode中的Input Sources,并且只有此Mode中的Observers會(huì)收到這個(gè)Runloop狀態(tài)變化的通知。屬于其他Mode集合中的sources、timers和observers會(huì)被掛起,這些Mode中的source只會(huì)讓新的事件掛起等待、timer會(huì)跳過(guò)本該定時(shí)回調(diào)的時(shí)機(jī)、observer也不會(huì)收到狀態(tài)變化的通知。直到你將這個(gè)Runloop的Mode切換成對(duì)應(yīng)的那個(gè),那么相應(yīng)的這些源才會(huì)正常接收事件,處理回調(diào),接收通知等。

具體說(shuō)一下Input Sources,蘋果官方文檔將Input Sources分成了基于端口的源Port-Based Sources、自定義源Custom Sources以及Cocoa選擇器源Cocoa Perform Selector Sources。再次強(qiáng)調(diào)的是,這三種輸入源都是異步地將事件傳遞給你的線程。

PORT-BASED SOURCES

Port-Based Sources是Cocoa和Core Foundation層自動(dòng)創(chuàng)建的,使用的是端口相關(guān)的對(duì)象和方法;這些源會(huì)監(jiān)控應(yīng)用程序的Mach端口,因此會(huì)自動(dòng)收到來(lái)自Kernel發(fā)出的信號(hào)通知。一般在Cocoa層,你不需要直接創(chuàng)建一個(gè)input source,而是簡(jiǎn)單地通過(guò)NSPort的方法來(lái)創(chuàng)建一個(gè)port對(duì)象然后加入到指定的runloop里,這個(gè)port對(duì)象會(huì)自動(dòng)配置創(chuàng)建所需要的輸入源;如果在Core Foundation層,你就需要手動(dòng)創(chuàng)建runloop的源以及對(duì)應(yīng)的port對(duì)象。

COCOA PERFORM SELECTOR SOURCES

按照蘋果文檔上描述,Cocoa Perform Selector Sources也屬于自定義源,這是Cocoa定義的一種通過(guò)使用Selector來(lái)進(jìn)行線程間通信的一種輸入源。這種源跟基于端口的源的一個(gè)區(qū)別是,當(dāng)一個(gè)perform selector source調(diào)用了其選擇器后,它會(huì)自動(dòng)從當(dāng)前runloop中把自己移除掉,而input sources會(huì)一直保持自己;但這種源跟基于端口的輸入源也有一個(gè)共同點(diǎn),就是這種源的請(qǐng)求是在線程中串行執(zhí)行的,這樣做的好處是當(dāng)很多方法在同一個(gè)線程被調(diào)用時(shí),可以避免一些同步的問(wèn)題。在新版本的OSX/iOS里你可以使用Perform Selector Sources將選擇器傳遞給任意線程(舊版只能適用于主線程),但前提是傳遞的目標(biāo)線程必須有一個(gè)激活了的runloop正在運(yùn)行,這樣才能成功監(jiān)聽接收這個(gè)源的事件。Runloop會(huì)在每一次循環(huán)時(shí)處理全部當(dāng)前正在在排隊(duì)的Selector事件,并不是一次循環(huán)處理一個(gè)事件。因此這種Perform Selector Sources是不能主動(dòng)喚醒runloop的,只能等待runloop被其他源喚醒后再對(duì)排隊(duì)的(queued)事件進(jìn)行處理。

TIMER SOURCES

Timer Sources定時(shí)器源會(huì)同步地把定時(shí)事件傳遞給指定線程。定時(shí)器其實(shí)就是一種定時(shí)通知線程自身何時(shí)需要處理事件的一種機(jī)制,只不過(guò)定時(shí)器是基于時(shí)間的,而Observers是基于runloop的狀態(tài)改變。值得注意的是,定時(shí)器的回調(diào)不一定是精確地每次都執(zhí)行,因?yàn)榧偃鐁unloop的mode被切換成當(dāng)前定時(shí)器沒有注冊(cè)的時(shí),這個(gè)定時(shí)器就會(huì)被掛起,直到當(dāng)前runloop的mode被換回來(lái);還有另一種情況,就是當(dāng)前runloop的一次循環(huán)中,系統(tǒng)在處理定時(shí)器回調(diào)之前一直在處理其他輸入源的回調(diào),這樣導(dǎo)致定時(shí)器設(shè)定的時(shí)間點(diǎn)可能被錯(cuò)過(guò),那么這個(gè)定時(shí)器在本次循環(huán)中也不會(huì)調(diào)用回調(diào)了,而是忽略掉這一次循環(huán)的回調(diào)機(jī)會(huì),等待下一次;當(dāng)然最后一種情況是,如果當(dāng)前runloop根本沒有啟用,那定時(shí)器更是永遠(yuǎn)都不會(huì)產(chǎn)生回調(diào)。

最后一種情況是,定時(shí)器在設(shè)定好的時(shí)間點(diǎn)因?yàn)樘囟ㄔ蛞淮位蜻B續(xù)數(shù)次沒有來(lái)得及發(fā)出回調(diào),而是產(chǎn)生了足夠大的延遲,那么定時(shí)器并不會(huì)錯(cuò)過(guò)幾次就補(bǔ)償幾次回調(diào),而是在數(shù)次延遲后僅調(diào)用一次回調(diào),然后就會(huì)等待下一次的預(yù)定時(shí)間點(diǎn)執(zhí)行。

OBSERVERS

輸入源是通過(guò)同步或異步的方式將事件傳遞給指定線程,而runloop觀察者會(huì)在runloop循環(huán)過(guò)程中特定位置、到達(dá)特定狀態(tài)時(shí)進(jìn)行調(diào)用。一般系統(tǒng)會(huì)在runloop即將休眠之前將本次循環(huán)中收到的所有渲染繪制、UI刷新、自動(dòng)釋放池出棧入棧事件統(tǒng)一處理。Runloop觀察者除了能夠觀察到一個(gè)“即將休眠”的狀態(tài)之外,還有很多對(duì)應(yīng)狀態(tài)可供觀察,以下:

即將處理定時(shí)器
即將處理一個(gè)輸入源
即將進(jìn)入休眠
剛被喚醒(尚未處理任何事件)
退出運(yùn)行循環(huán)```

你只能使用Core Foundation手動(dòng)給當(dāng)前線程添加觀察者,同時(shí)你也需要指定這個(gè)觀察者是一次性的還是需要重復(fù)使用的。因?yàn)橛^察者也類似于定時(shí)器,如果設(shè)定為一次性的,那只要它被調(diào)用一次之后就會(huì)將自身移除。

















最后編輯于
?著作權(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)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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