在說RunLoop之前,先來了解一下iOS的系統(tǒng)架構(gòu),蘋果官方將整個(gè)系統(tǒng)大致分為四個(gè)層次---應(yīng)用層,應(yīng)用架構(gòu)層,核心架構(gòu)層和Darwin。Mach是Darwin的組成部分,Mach提供的API非常的少,但這些API非常的基礎(chǔ),如果沒有這些API的話,其他的任何工作是無法實(shí)施的。(廢話嗎,寫在操作系統(tǒng)核心的東西能不基礎(chǔ)么)。而RunLoop的核心其實(shí)就是mach_msg(),如果沒有別人發(fā)送 port 消息過來,內(nèi)核會(huì)將線程置于等待狀態(tài)。
一般來說,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完成后線程就會(huì)退出,那么加入我們點(diǎn)開一個(gè)應(yīng)用,執(zhí)行一次后就不能再執(zhí)行了,而我們需要的是,當(dāng)我們?cè)俅吸c(diǎn)擊時(shí),程序仍然可以相應(yīng)我們的操作,這就需要一個(gè)機(jī)制,讓線程可以隨時(shí)處理事件而且不退出。邏輯很簡(jiǎn)單,只是一個(gè)循環(huán)就可以了,實(shí)現(xiàn)這種模型的關(guān)鍵點(diǎn)在于:如何管理事件/消息,如何讓線程在沒有處理消息時(shí)休眠以避免資源占用、在有消息到來時(shí)立刻被喚醒。
RunLoop實(shí)際上是一個(gè)對(duì)象,這個(gè)對(duì)象管理了其需要處理的消息。并提供了一個(gè)入口函數(shù)來執(zhí)行它的邏輯。線程執(zhí)行了這個(gè)函數(shù)后,就會(huì)一直處于這個(gè)函數(shù)內(nèi)部 "接受消息->等待->處理" 的循環(huán)中,直到這個(gè)循環(huán)結(jié)束(比如傳入 quit 的消息),函數(shù)返回。
蘋果是不允許我們直接創(chuàng)建RunLoop的,它給我們提供了兩個(gè)函數(shù),CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。從字面意思就可以看出,一個(gè)是獲取主RunLoop,一個(gè)是獲取當(dāng)前的RunLoop。線程和 RunLoop 之間是一一對(duì)應(yīng)的,其關(guān)系是保存在一個(gè)全局的 Dictionary 里。線程作為Key,RunLoop作為value,線程剛創(chuàng)建時(shí)并沒有 RunLoop,如果你不主動(dòng)獲取,那它一直都不會(huì)有。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí),RunLoop 的銷毀是發(fā)生在線程結(jié)束時(shí)。你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop。
RunLoop有一個(gè)類叫CFRunLoopModeRef,蘋果為我們提供了這個(gè)類的API,一個(gè)RunLoop可以有很多個(gè)Mode,每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode。我們最常見的是kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。DefaultMode 是默認(rèn)狀態(tài)的,TrackingRunLoopMode 是追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)。我們非常常見的一種狀態(tài)有創(chuàng)建一個(gè)Timer,把這個(gè)Timer加入到DefaultMode里,當(dāng)Scrollview或者其子類進(jìn)行滑動(dòng)時(shí),Timer就會(huì)停止,就是因?yàn)镾crollview在滑動(dòng)時(shí),RunLoop 將 mode 切換為了 TrackingRunLoopMode。
再來談一下我對(duì)AutoReleasepool和RunLoop的一些關(guān)系。在App啟動(dòng)以后,蘋果在主線程RunLoop里會(huì)注冊(cè)兩個(gè)Observe,一個(gè)Observe監(jiān)視的事件是即將進(jìn)入Loop,另一個(gè)observe監(jiān)視的是即將進(jìn)入休眠和退出Loop,在第一個(gè)observe在回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池。其 order 是-2147483647,優(yōu)先級(jí)最高,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。第二個(gè) Observer在 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)先級(jí)最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后。這也就保證了我們?cè)谥骶€程里寫的一些代碼會(huì)永遠(yuǎn)被RunLoop創(chuàng)建好的Autoreleasepool包圍著,避免的內(nèi)存的泄露,同時(shí)也方便了我們開發(fā)者。實(shí)際上 RunLoop 底層也會(huì)用到 GCD 的東西,比如 RunLoop 是用 dispatch_source_t 實(shí)現(xiàn)的 Timer。但同時(shí) GCD 提供的某些接口也用到了 RunLoop, 例如 dispatch_async()。
參考文章:http://www.cocoachina.com/cms/wap.php?action=article&id=11970
我對(duì)RunLoop的一點(diǎn)理解
最后編輯于 :
?著作權(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ù)。
【社區(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)容
- Run loop 剖析:Runloop 接收的輸入事件來自兩種不同的源:輸入源(intput source)和定時(shí)...