什么是RunLoop?
答:RunLoop是線程相關(guān)的基礎(chǔ)框架中的一部分,是一個事件處理對象丸边,每一個線程都有與之對應(yīng)的RunLoop典格,但并不是線程創(chuàng)建時就有RunLoop,只有當(dāng)前線程第一次主動獲取RunLoop抵皱,系統(tǒng)才會創(chuàng)建當(dāng)前線程相應(yīng)的RunLoop善榛。
RunLoop的作用是什么?
答:1).管理線程的生命周期及活動呻畸。 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ??
? ? ?2).處理輸入事件源以及通知觀察者移盆。
如何使用RunLoop?
答:iOS/OSX提供了Core Foundation(CFRunLoopRef)和Cocoa(NSRunLoop)兩套API來使用RunLoop,可以通過CFRunLoopGetMain() 和 CFRunLoopGetCurrent()或[NSRunLoop mainRunLoop]和[NSRunLoop currentRunLoop]來獲取RunLoop對象伤为,NSRunLoop是基于CFRunLoopRef的高層組件咒循,CFRunLoopRef的API都是線程安全的,但NSRunLoop的API不是線程安全的绞愚。雖然CFRunLoopRef的操作都是線程安全的叙甸,但不建議跨線程處理。
如何啟動RunLoop位衩?
答:可以通過NSRunLoop對象的run裆蒸,runUntilDate:,runMode:beforeDate:方法糖驴,或通過CFRunLoopRef的CFRunLoopRun()僚祷,CFRunLoopRunInMode()來啟動RunLoop。
使用NSRunLoop和CFRunLoopRef來啟動RunLoop有何不同遂赠?
答:雖然NSRunLoop是基于CFRunLoopRef的高層組件,對NSRunLoop的操作最終都會轉(zhuǎn)換成對CFRunLoopRef的操作晌杰,但需要注意的是跷睦,NSRunLoop與CFRunLoopRef并不是簡單的接口轉(zhuǎn)換,就像啟動RunLoop一樣肋演,NSRunLoop的Run方法與CFRunLoopRun()并不是對應(yīng)著的抑诸,而且有著一定的區(qū)別。
如何退出RunLoop爹殊?
答:退出RunLoop的方式有三種蜕乡,分別是:方式一.給RunLoop設(shè)定超時時間;方式二.使用CFRunLoopStop()函數(shù)顯式退出梗夸;方式三.移除CurrentMode的所有輸入源和定時源层玲。
方式一是官法推薦的方式,可以安全有效地退出RunLoop反症。方式二辛块,用CFRunLoopStop()函數(shù)并不是絕對可以退出Run的,要看以什么方式啟動RunLoop铅碍,如果用[[NSRunLoop currentRunLoop] run]來啟動RunLoop润绵,那么用CFRunLoopStop()是無法退出RunLoop的,正如上面的偽代碼所示胞谈,以[[NSRunLoop currentRunLoop] run]啟動的RunLoop尘盼,唯一退出的方式是移除CurrentMode的所有輸入源和定時源憨愉,也就是方式三,但這種方式是不穩(wěn)定的卿捎,因?yàn)橄到y(tǒng)會添加一些輸入源或定時源來完成一些操作配紫。
RunLoop是否自動運(yùn)行的?
答:所有線程的RunLoop都是默認(rèn)不啟動的娇澎,但主線程的RunLoop會隨著應(yīng)用的運(yùn)行而被啟動笨蚁。
RunLoop處理的輸入事件源有哪些?
答:RunLoop處理的事件源有兩種趟庄,分別是輸入源(Input Source)和定時源(Timer Source)括细,而主線程的Main RunLoop還會處理GCD事件源。
什么是RunLoopMode戚啥?
答:RunLoop可以有多個RunLoopMode奋单,RunLoopMode包含了輸入源(Input Source),定時源(Timer Source)和觀察者(Observer)猫十。
RunLoop每次進(jìn)入Run時都需要指定一個RunLoopMode览濒,指定RunLoopMode后,只有當(dāng)前RunLoopMode內(nèi)的源和觀察者會被處理拖云,其它的源和觀察者需要等到RunLoop運(yùn)行其RunLoopMode時才會被處理贷笛,切換RunLoopMode的唯一方式是,退出當(dāng)前RunLoopMode宙项,重新指定一個RunLoopMode進(jìn)入Run乏苦。當(dāng)RunLoop選擇一個RunLoopMode進(jìn)入Run時,若這個RunLoopMode中并沒有需要處理的源(輸入源或定時源)尤筐,RunLoop就會直接退出汇荐。
RunLoopMode中的_timerPort是_timers中所有定時源的公共Port,_portToV1SourceMap記錄了_sources1以及對應(yīng)的Port盆繁,通過Port獲取相應(yīng)的Source1掀淘。
什么是CommonMode和CommonModeItem?
答:CommonModeItem是公共ModeItem油昂,CommonMode是公共RunLoopMode革娄,當(dāng)把一個RunLoopMode注冊為CommonMode時,CommonModeItem被會自動添加到CommonMode里冕碟,當(dāng)CommonModeItem有所改動時稠腊,CommonMode也會作出相應(yīng)的改動。
什么是定時源(Timer Source)鸣哀?
答:用于延時或重復(fù)的時間間隔處理事件架忌。
如何使用定時源?
答:CFRunLoopTimerRef是RunLoop中唯一的定時源我衬,以定時器的形式表示和使用叹放,可選擇的定時器有饰恕,NSTimer,CADisplayLink井仰,CFRunLoopTimerRef埋嵌,GCD Timer。
四種定時器的區(qū)別是什么俱恶?
答:NSTimer和CADisplayLink是Cocoa提供的高層定時器雹嗦,CFRunLoopTimerRef是Core Foundation提供的基礎(chǔ)定時器,NSTimer和CADisplayLink是建立在CFRunLoopTimerRef之上的高層組件合是,而CFRunLoopTimerRef是建立在mk_timer之上了罪。NSTimer和CADisplayLink主要區(qū)別在于信號的發(fā)射頻率不同,CADisplayLink的信號發(fā)射頻率固定在16.67ms一次聪全,而NSTimer的信號發(fā)射頻率可自由定義(具體請看iOS10定時消息的改動)泊藕。
GCD Timer有別于前三種定時器,是由GCD系統(tǒng)所管理的定時器难礼,通過一定的時間間隔dispatch任務(wù)到相應(yīng)的隊列中處理娃圆,以主線程為例,時間間隔到達(dá)后蛾茉,GCD系統(tǒng)將Block dispatch到主線程對應(yīng)的Main Queue讼呢,等待Main RunLoop檢測和處理。
如何選擇使用哪一種定時器谦炬?
答:除非需要實(shí)現(xiàn)定時動畫悦屏,否則不建議使用CADisplayLink作為定時器(具體請看iOS10定時消息的改動。什么是定時動畫吧寺?請查看iOS動畫的基礎(chǔ)知識)窜管;NSTimer適用于大部份情況散劫,但需要注意循環(huán)引用的問題稚机;GCD Timer的缺點(diǎn)在于,不能在自己所創(chuàng)建的子線程中使用获搏。
CFRunLoopTimerRef的觸發(fā)原理是怎樣的赖条?
答:具體請看iOS10定時消息的改動。
什么是輸入源常熙?
答:是RunLoop所處理的事件源之一纬乍,主要用于線程或進(jìn)程交互,輸入源分為基于端口輸入源(Source1)和非端口輸入源(Source0)裸卫。
基于端口輸入源(Source1)與非端口輸入源(Source0)的區(qū)別是什么仿贬?
答:1).Source0與Source1都是CFRunLoopSourceRef類型,但配置方式不同墓贿,Source0用CFRunLoopSourceContext來配置茧泪,Source1用CFRunLoopSourceContext1來配置蜓氨。
? ? ?2).Source0與Source1都可用于線程(或進(jìn)程)交互,但交互的形式有所不同队伟,Source1監(jiān)聽端口穴吹,當(dāng)端口有消息到達(dá)時,相應(yīng)的Source1就會被觸發(fā)回調(diào)嗜侮,完成相應(yīng)的操作港令;而Source0并不監(jiān)聽端口,讓Source0執(zhí)行回調(diào)需要手動標(biāo)記Source0為待處理狀態(tài)锈颗,還需要呼醒Source0所在的RunLoop顷霹。
? ? ? 3).從Source0與Source1的交式方式了解到,Source1的交互會主動呼醒所在的RunLoop宜猜,而Source0的交互則需要依賴其它線程來呼醒Source0所在的RunLoop泼返。
? ? ? 4).一次Loop只能執(zhí)行一個Source1的回調(diào),但一次Loop可以執(zhí)行多個待處理的Source0的回調(diào)姨拥。
如何創(chuàng)建Source0?
如何標(biāo)記Source為待處理狀態(tài)绅喉,且呼醒所在的RunLoop?
如何創(chuàng)建Source1以及如何交互?
答:有Cocoa和Core Foundation兩套API來配置和使用Source1叫乌;Cocoa有NSPort柴罐,NSMachPort,NSMessagePort憨奸,NSSocketPort等類革屠,Core Foundation有CFMachPortRef,CFMessagePortRef排宰,CFSocketRef等似芝。其中用得比較多的是NSMachPort和CFMessagePortRef。
Cocoa所提供的類只是建立在Core Foundation之上的高層組件板甘,且提供了toll-free bridged党瓮。需要注意的是,NSMachPort接收和發(fā)送需要是同一個對象盐类;CFMessagePortRef接收和發(fā)送的Port所用的name要相同寞奸,CFMessagePortSendRequest()函數(shù)通過CFMessagePortRef的name來查找相應(yīng)的接收端口來進(jìn)行消息發(fā)送(不建議直接使用mach_msg()來發(fā)送消息,關(guān)于Port可以查看Inter-Process Communication)在跳。
RunLoop的內(nèi)部邏輯是怎樣的枪萄?
需要注意的是第五步,官方文檔寫的是如果有基于端口的輸入源待處理猫妙,就進(jìn)入第九步瓷翻,這跟CFRunLoop的源碼不同。
從源碼可以看到,第五步檢測的是GCD端口事件齐帚,而不是官方文檔所寫的基于端口的輸入源元践,但經(jīng)過大量測試發(fā)現(xiàn),第五步實(shí)際上會檢測所有未處理的端口事件童谒,而并非像官方文檔或源碼所展示的那樣(太坑爹了单旁,居然官方文檔跟源碼不同,源碼又和實(shí)際測試結(jié)果不同??)饥伊。
如果有什么地方寫錯的麻煩指出象浑,如果有什么還想知道的請在評論留言,我會盡快補(bǔ)上的琅豆。