一腊状、什么是Runloop?
Runloop是通過內(nèi)部維護(hù)的事件循環(huán)苔可,來對事件\消息進(jìn)行管理的對象缴挖。
二、什么是事件循環(huán)焚辅?
有消息需要的處理的時(shí)映屋,立即被喚醒,(內(nèi)核態(tài)-->用戶態(tài))
沒有消息需要處理時(shí)同蜻,進(jìn)入休眠狀態(tài)棚点,避免資源占用。(用戶態(tài)-->內(nèi)核態(tài))
維護(hù)的事件循環(huán):可以用來不斷的處理消息或者事件湾蔓,然后對他們進(jìn)行管理瘫析,同時(shí),當(dāng)沒有消息需要處理時(shí)默责,會(huì)從用戶態(tài)到內(nèi)核態(tài)的一個(gè)切換贬循,以此進(jìn)行當(dāng)前線程的休眠,避免資源占用傻丝。同時(shí)當(dāng)有消息需要處理時(shí)甘有,就會(huì)發(fā)生從內(nèi)核態(tài)到用戶態(tài)的轉(zhuǎn)換诉儒。當(dāng)前的用戶線程會(huì)被喚醒葡缰。
三、我們的main 函數(shù)為什么可以一直處于活躍狀態(tài)忱反,不退出泛释?
在main 函數(shù)中多調(diào)用的UIApplicationMain 這個(gè)函數(shù)內(nèi)部,會(huì)啟動(dòng)主線程runloop,runloop是對事件循環(huán)的維護(hù)機(jī)制温算×#可以在有事做,做事注竿,沒有事做茄茁,進(jìn)行用戶態(tài)到內(nèi)核態(tài)的切換魂贬,進(jìn)入休眠,避免占用資源裙顽。
四付燥、Runloop 的數(shù)據(jù)結(jié)構(gòu):
NSRunLoop(fundation) 是CFRunLoop的封裝,提供了面向?qū)ο蟮腁PI.
(1)NSRunLoop 的數(shù)據(jù)結(jié)構(gòu):
- CFRunLoop
- CFRunLoopMode
- Timer/Observer/Source
(2)CFRunLoop 結(jié)構(gòu)體
CFRunLoop包含有:
- pthread 線程與runloop 是一一對應(yīng)的關(guān)系愈犹。
- currentMode键科、------>CFRunLoopMode 的數(shù)據(jù)結(jié)構(gòu)。
- modes(多個(gè)mode的集合) ----> NSMutableSet< CFRunLoopMode *>
- commonModes----->NSMutableSet< NSString *>
- commonModeItems --->包含多個(gè)timer,多個(gè)source,多個(gè)observer漩怎。
注意:runloop與mode 是一對多的關(guān)系勋颖。
** CFRunLoopMode結(jié)構(gòu)**
- name. ====> NSDefaultRunLoopMode(別名定義的字符串)
- sources0
- soources1
- observers
- timers
CFRunLoopSource結(jié)構(gòu)
- source0 需要手動(dòng)喚醒線程能力。
- source1 具備喚醒線程的能力勋锤。
CFRunLoopTimer
事件定時(shí)器:和NSTimer 是toll-free bridge的饭玲。
CFRunLoopObserver結(jié)構(gòu)體
觀測時(shí)間點(diǎn):
- kCFRunLoopEntry. 將要進(jìn)入runloop 中。
- kCFRunLoopBeforeTimers 將要處理timers 的相關(guān)問題
- kCFRunLoopBeforeSources 將要處理source相關(guān)問題叁执。
- kCFRunLoopBeforeWaiting 通知觀察者將要進(jìn)入休眠狀態(tài)(即將:用戶態(tài)-->內(nèi)核態(tài))
- kCFRunLoopAfterWaiting 內(nèi)核態(tài)-->用戶態(tài)的不久時(shí)間點(diǎn)咱枉。
- kCFRunLoopExit. 退出runloop.
數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系:
- runloop與其mode方式是一對多的方式,而Mode與source /timer/observer都是一對多的徒恋。
五蚕断、RunLoopMode 的數(shù)據(jù)結(jié)構(gòu):
mode為什么會(huì)有多個(gè)?入挣?
當(dāng)我們的runloop運(yùn)行在某個(gè)mode1時(shí)亿乳,另一個(gè)mode2上的timer事件進(jìn)行回調(diào)了。這是runloop是無法接收對應(yīng)mode2 中的事件回調(diào)径筏。起到了屏蔽的效果葛假。
如何將一個(gè)Timer添加到不同的mode下?滋恬?可以使用CommonMode.
CommonMode結(jié)構(gòu)特性
聊训??
CommonMode 是同步Source\Timer\Observer到多個(gè)mode的解決方案恢氯。
六带斑、事件循環(huán)機(jī)制:
在runloop啟動(dòng)后,會(huì)通過通知勋拟,告訴觀察者即將進(jìn)入runloop. 之后勋磕,將要處理timer/source0事件通知。之后進(jìn)入到正式的source0事件處理敢靡。如果有Source1 ,那么會(huì)通過goto語法挂滓,處理喚醒時(shí),收到的消息啸胧。如果沒有什么要處理的赶站。就會(huì)進(jìn)入休眠幔虏。喚醒操作:timer/source1/外部手動(dòng)喚醒。線程被喚醒后贝椿,會(huì)觸發(fā)觀察者所计,告訴觀察者,線程被喚醒了团秽。即將推出RunLoop.
七主胧、RunLoop 與NSTimer
(1)滑動(dòng)TableView 的時(shí)候,我們的定時(shí)器還會(huì)生效嗎习勤?踪栋?
正常情況下,是kCFRunLoopDefaultModel.當(dāng)滑動(dòng)時(shí)進(jìn)行了mode 轉(zhuǎn)換图毕,轉(zhuǎn)換成了UITractingRunLoopMode;
通過CFRunLoopAddTimer(runloop, timer,commonMode)commonMode 不是實(shí)際的Mode,只是將Mode打上一個(gè)CommonMode 的標(biāo)記夷都。就可以將一個(gè)事件元,同步到多個(gè)Mode 中予颤。
八囤官、線程與RunLoop
(1)自己創(chuàng)建的線程是沒有runloop的需要我們手動(dòng)創(chuàng)建。
(2)如何實(shí)現(xiàn)一個(gè)常駐線程蛤虐?
- 為當(dāng)前線程開啟RunLoop.(調(diào)用獲取RunLoop 的方法党饮,因?yàn)楂@取RunLoop的方法,會(huì)判定是否有驳庭,沒有就進(jìn)行創(chuàng)建)
- 向該runloop中添加一個(gè)port/source 等用來維護(hù)RunLoop事件循環(huán)刑顺。(資源事件元,需要處理的話饲常,默認(rèn)情況下蹲堂,是不能維持事件循環(huán)的)
- 啟動(dòng)RunLoop.
1.創(chuàng)建一個(gè)Source
- 創(chuàng)建RunLoop 并將Source 添加到runLoop 中。(CFRunLoopAddSource)
3贝淤。維護(hù)循環(huán)柒竞,再循環(huán)中創(chuàng)建AutoReleasePool,并調(diào)用CFRunLoopRunInMode 方法。**注意:添加資源的Mode,與運(yùn)行時(shí)Mode的必須是同一個(gè)播聪。
4.移除source,調(diào)用CFRunLoopRemoveSource(某一時(shí)機(jī)可以保證RunLoop線程推出朽基。)
5.釋放資源CFRelease(source)避免內(nèi)存泄漏。
(3)調(diào)用RunLoop的run方法后犬耻,系統(tǒng)會(huì)調(diào)用mach_message方法踩晶,發(fā)生了用戶態(tài)向核心態(tài)的切換执泰。當(dāng)前線程處于休眠狀態(tài)枕磁。
(4)如何保證子線程數(shù)據(jù)更新UI的時(shí)候,不打斷用戶滑動(dòng)操作术吝?
分析:在用戶處于滑動(dòng)的情況下计济,處于RunLoop的UITractingMode的模式下茸苇。因此,當(dāng)子線程獲取到數(shù)據(jù)需要顯示UI時(shí)沦寂,我們可以將數(shù)據(jù)進(jìn)行包裝后提交到住線程的DefaultMode 模式下学密。這樣當(dāng)滑動(dòng)時(shí),由于處于TractingMode下传藏,就不會(huì)對其進(jìn)行處理腻暮,知道滑動(dòng)停止后,回到defaultMode時(shí)毯侦,才開始對其進(jìn)行處理哭靖,這樣就不會(huì)打斷用戶的滑動(dòng)效果了。
實(shí)現(xiàn)原理:當(dāng)App啟動(dòng)時(shí)侈离,會(huì)調(diào)用UIApplicationMain,啟動(dòng)主線程中的RunLoop.
經(jīng)過一些列處理后试幽,runloop 將會(huì)處于休眠狀態(tài),此時(shí)卦碾,我點(diǎn)擊屏幕铺坞,就會(huì)產(chǎn)生一個(gè)MachPort,machport會(huì)轉(zhuǎn)化成Source1,喚醒主線程洲胖,運(yùn)行處理济榨,之后,當(dāng)App殺死绿映,就會(huì)發(fā)生runloop 的退出腿短。也會(huì)發(fā)出一個(gè)通知,即將退出runloop 绘梦,退出后橘忱,線程被銷毀。
RunLoop 核心問題卸奉?