RunLoop是通過內(nèi)部維護的事件循環(huán)
來對事件和消息進行管理的對象
- 沒有消息需要處理時,Runloop將線程控制器交給系統(tǒng)牧嫉,即從
用戶態(tài)->內(nèi)核態(tài)
,休眠以避免資源占用 - 有消息需要被處理時竭翠,立即喚醒,又從
內(nèi)核態(tài)->用戶態(tài)
嘉汰。
Runloop核心
Runloop可以簡單地理解成一個while(true)
的循環(huán)皿曲,但又不是while循環(huán)這么簡單,因為這樣的實現(xiàn)會使CPU進行大量無謂的空轉(zhuǎn)粤铭。所以挖胃,Runloop機制的核心就是保證線程在有events需要處理時能喚醒,在沒有events時能進行休眠。
而實現(xiàn)真正的休眠酱鸭,是靠沒有events時從用戶態(tài)->內(nèi)核態(tài)
實現(xiàn)的吗垮,當(dāng)有事件時,系統(tǒng)內(nèi)核通過mach_msg()
或者mach port
方法將事件發(fā)送給對應(yīng)的Runloop凹髓,Runloop收到事件后從休眠狀態(tài)切換到喚醒狀態(tài)烁登,并從內(nèi)核態(tài)->用戶態(tài)
。
數(shù)據(jù)結(jié)構(gòu)
NSRunLoop是CFRunLoop的封裝蔚舀,提供了面向?qū)ο蟮腁PI
- CFRunLoop
- CFRunLoopMode
- Source/Timer/Observer
CFRunLoop{
pthread -----> 線程和runloop是一一對應(yīng)的
currentMode ---> CFRunLoopMode
modes ---> NSMutableSet<CFRunLoopMode*>
commonModes ---> NSMutableSet<NSString*>
commonModeItems ---> {多個Observer饵沧,Timer, Source}
}
CFRunLoopMode
CFRunLoopMode內(nèi)部數(shù)據(jù)結(jié)構(gòu)如下
CFRunLoopMode {
name (如NSDefaultRunLoopMode)
sources0 ---> NSMutableSet
sources1 ---> NSMutableSet
observers ---> NSMutableArray
timers ---> NSMutableArray
}
mode是管理著Runloop與source/timer/observer之間的橋梁赌躺,在一開始會注冊五個mode
- kCFRunLoopDefaultMode: App的默認(rèn) Mode狼牺,通常主線程是在這個 Mode 下運行的。
- UITrackingRunLoopMode: 界面跟蹤 Mode寿谴,用于 ScrollView 追蹤觸摸滑動锁右,保證界面滑動時不受其他 Mode 影響。默認(rèn)NSTimer是被加入到default mode中的讶泰,所以當(dāng)滑動時Runloop切換到tracking mode,這時default mode中的Timer回調(diào)不會被調(diào)用拂到,所以NSTimer的精度沒有CADisplayLinker高痪署。
- UIInitializationRunLoopMode: 在剛啟動 App 時進入的第一個 Mode,啟動完成后就不再使用兄旬。
- GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode狼犯,通常用不到。
-
kCFRunLoopCommonModes/NSRunLoopCommonModes:
1.這是一個占位的 Mode领铐,不是實際存在的
一種mode悯森。
2.是同步Source/Timer/Observer到多個Mode中的一種技術(shù)方案
如果需要將事件加入到多個mode中,則將它注冊到commonMode中绪撵,該mode實際上是多個mode的集合瓢姻。
出于將source/timer/observer分隔開的目的,RunLoop一次只能運行在一個mode下音诈,當(dāng)運行時在RunLoop的currentMode屬性中會標(biāo)記當(dāng)前運行的mode幻碱。而當(dāng)要切換mode時,RunLoop必須先退出细溅,并選中一個mode重新進入褥傍,達到切換mode的目的。在切換mode時喇聊,被加入到commonModes中的事件會被拷貝一次到運行的mode中恍风。
CFRunloopObserver
觀測時間點
- kCFRunLoopEntry 將進入runloop
- kCFRunLoopBeforeTimers
- kCFRunLoopBeforeSouces
- kCFRunLoopBeforeWaiting 將要進入休眠狀態(tài)(即將從用戶態(tài)切換到內(nèi)核態(tài)
- kCFRunLoopAfterWaiting 開始喚醒(從內(nèi)核態(tài)進入到用戶態(tài)不久
kCFRunLoopExit runloop退出的通知
各個數(shù)據(jù)結(jié)構(gòu)之間的關(guān)系?
RunLoop和線程一一對應(yīng)--->多個mode--->{多個Source,Timer朋贬,Observer}
如何喚醒Runloop
1. CFRunLoopSource
source0: 需要手動喚醒線程
該類Source是App的內(nèi)部事件凯楔,不具有獨立喚醒Runloop的能力。一個Source0需要被處理時兄世,他需要被CFRunLoopSourceSignal()
函數(shù)標(biāo)記為待處理啼辣,并調(diào)用CFRunLoopWakeUp
函數(shù)來喚醒Runloop,CFRunLoopWakeUp
函數(shù)內(nèi)部通過一個_wakeUpPort
成員變量來喚醒Runloop御滩,推測該變量是一個mach port鸥拧,Runloop只有通過mach port
與mach_msg()
才可以喚醒。喚醒后通過調(diào)用__CFRunLoopDoSources0函數(shù)來處理Source0事件削解,并在之后將該事件標(biāo)記為已處理富弦。
source1: 具備喚醒線程的能力
一般是由硬件生成的source,如觸摸氛驮、點擊腕柜、搖晃、旋轉(zhuǎn)等矫废。此類Source可喚醒Runloop盏缤。
2.Timer
使用NSTimer API注冊執(zhí)行的任務(wù),就屬于這一類
3. Observer
某個Observer可以監(jiān)聽runloop的狀態(tài)變化蓖扑,并作出反應(yīng)