我的面試---RunLoop整理

  • 程序的啟動順序
  • main函數(shù)為什么不會退出
  • autoreleasePool 在何時被釋放?
  • 事件的響應(yīng)過程
  • 手勢識別的過程
  • RunLoop的簡介
  • 線程與runloop的關(guān)系
  • RunLoop的機(jī)制
  • RunLoop的五種模式
  • RunLoop和NSTimer
    *滑動tableView的時候,定時器會生效嗎?
    • 為什么timer不好使?
    • 解釋下NSTimer?
    • NSTimer和CADisplayLink哪一個更準(zhǔn)確
    • NSTimer的循環(huán)引用
  • 怎么創(chuàng)建一個常駐線程
  • 怎么保證子線程數(shù)據(jù)請求后不打斷滑動
  • AFNetWoring中如何運(yùn)用RunLoop
  • performSelector的實(shí)現(xiàn)原理
  • 利用RunLoop解釋一下頁面渲染的過程

程序的啟動順序

main函數(shù)執(zhí)行前
1.程序啟動時,系統(tǒng)會讀取程序的可執(zhí)行文件,從中獲取動態(tài)加載器的路徑。
2.加載動態(tài)加載器, 初始化運(yùn)行環(huán)境。并將其加入到內(nèi)存中
3.動態(tài)鏈接依賴庫,初始化依賴庫,初始化runtime。
4.runtime對所有的類進(jìn)行類結(jié)構(gòu)的初始化。并調(diào)用所有的load方法。
5.動態(tài)加載器返回main函數(shù)地址,進(jìn)入main函數(shù)。
mian函數(shù)之后
1.調(diào)用UIApplicationMain函數(shù)初始化UIApplication和UIApplicationDelegate.
2.開啟事件循環(huán)Runloop,并監(jiān)聽系統(tǒng)事件
3.通知delegate并調(diào)用delegate的didfinshLunching。創(chuàng)建UIWindow,設(shè)置rootViewController,調(diào)用makeKeyAndvisible顯示。


main函數(shù)為什么不會退出

UIApplicationMain內(nèi)部默認(rèn)開啟了主線程的runloop,并執(zhí)行了一段循環(huán)代碼,mian函數(shù)不會返回,會不斷接收處理消息/事件,以及休眠


autoreleasePool 在何時被釋放?

  • App啟動時 蘋果會注冊兩個Obsever,其回調(diào)是_wrapRunLoopWithAutoRealasePoolHandler()
  • 第一個Obsever監(jiān)聽RunLoop即將進(jìn)入的時候,在其回調(diào)內(nèi)會調(diào)用AutoRealsePoolPush,創(chuàng)建一個新的自動釋放池。其自動釋放池的優(yōu)先級最高。保證其回調(diào)是在其他回調(diào)之前的。
  • 第二個Observer監(jiān)聽兩個事件

事件的響應(yīng)過程

1.蘋果注冊了一個source1(基于mach port的),用來接收系統(tǒng)消息,其回調(diào)是IOHIDEventSystemClientQueueCallBack()
2.當(dāng)一個硬件事件發(fā)生,首先io.framework會生成一個IOHIDEvent由springboard接收,隨后由machport轉(zhuǎn)發(fā)給對應(yīng)的app,此時蘋果注冊的source1回調(diào)將會被觸發(fā),將其加入到UIApplication事件隊(duì)列中。
3.UIApplication事件隊(duì)列將HIDEvent封裝成UIEvent,后進(jìn)行出了或者分發(fā)。


手勢識別的過程

當(dāng)UIApplicationHandleEventQueue識別了一個手勢時,先使用cancel打斷手勢的系統(tǒng)回調(diào),并將其標(biāo)記為待處理。蘋果注冊了一個observer監(jiān)視runloop即將要休眠的狀態(tài),在RunLoop即將要休眠的時候。在observer回調(diào)內(nèi)會獲取所有待處理的手勢,并執(zhí)行UIGesture的回調(diào)。


RunLoop的簡介

RunLoop是通過內(nèi)部維護(hù)事件循環(huán)來對事件/消息進(jìn)行管理的一個對象
RunLoop在沒有消息時休眠,避免資源浪費(fèi)
在有消息時立即喚醒。

線程與RunLoop的關(guān)系

  • 線程和runloop是一對一的關(guān)系,一個線程對應(yīng)一個RunLoop。
  • 主線程默認(rèn)開啟了RunLoop,子線程中的RunLoop是以懶加載的方式處理的
  • 線程和runloop是存在一個全局的表中,key是線程,runloop是value。

RunLoop的機(jī)制

  • 通知觀察者RunLoop將要啟動。
  • 通知觀察者即將要處理timers。
  • 通知觀察者即將要處理非基于port的事件。
  • 通知觀察者正在處理非基于port的事件。
  • 如果基于port的source1已經(jīng)準(zhǔn)備好并處于等待狀態(tài),就會處理source1的事件(處理喚醒時收到的事件)
  • 通知觀察者即將要進(jìn)入休眠狀態(tài)
  • 通知觀察者 即將要被喚醒(被喚醒的條件)
    * timer的時間到了
    * 接收了一個source1事件
    * runloop自身超時了
    * 被其他調(diào)用者手動喚醒
  • 處理喚醒時收到的事件
  • 通知觀察者runLoop即將要結(jié)束

RunLoop的五種模式

KCFRunLoopDefaultMode
UITrackingRunLoopMode
KCFRunLoopCommonsModes
UIInitalatizationRunLoopMode
GSEventReciveMode

RunLoop和NSTimer

1.滑動tableView的時候,定時器會生效嗎?
定時器不會生效。默認(rèn)情況下RunLoop是運(yùn)行在KCFRunLoopDefaultMode下的。tableView滑動時會切換至UITrackingMode下,此時就無法接收Timer的事件
解決辦法:
將定時器添加到KCFRunLoopCommonsModes下。
[[NSRunLoop currentRunLoop] addTimer:time forModel:KCFRunLoopCommonsModes];

2.為什么timer不好使?
默認(rèn)情況下NSTimer是添加到DefaultMode下的,當(dāng)RunLoop切換到其他的Model,就無法接收到NSTimer的事件。
3.解釋下NSTimer?
NSTimer其實(shí)就是KCFRunLoopTimerRef.一個NSTimer注冊到RunLoop后,Runloop會為其重復(fù)的時間點(diǎn)注冊好事件。為了節(jié)省資源RunLoop不會在非常準(zhǔn)確的時間點(diǎn)回調(diào)NSTimer,如果錯過了不會延遲執(zhí)行。
4、NSTimer和CADisplayLink哪一個更準(zhǔn)確

  • CADisplyLink更準(zhǔn)確
  • NSTimer同上
  • CADisplayLink是一個和屏幕刷新率一致的定時器。iOS設(shè)備的屏幕刷新頻率是固定的。CADisplyLink每次刷新結(jié)束后都會被調(diào)用,精確度非常高
  • CADisplyLink使用的場合相對專一,適合做UI的不停重繪。(自定義動畫引擎和視頻播放渲染) NSTimer使用比較廣泛(需要單次或者循環(huán)定時處理的任務(wù))。
    5.NSTimer的循環(huán)引用*
    在控制器內(nèi)創(chuàng)建NSTimer作為其屬性,由于定時器創(chuàng)建后也會強(qiáng)引用該控制器,那么該對象和定時器就相互引用了
    如何解決?
    可以手動斷開循環(huán)引用。
    如果是不重復(fù)定時器,在其方法里將定時器invalidate并設(shè)置為nil。
    如果是重復(fù)的定時器,在合適的位置將其invalidate并設(shè)置為nil即可
    或者將self 設(shè)置為weakSelf

怎么創(chuàng)建一個常駐線程

為當(dāng)前線程開啟一個runloop,并添加一個事件或者消息。并調(diào)用run方法運(yùn)行


怎么保證子線程數(shù)據(jù)請求后不打斷滑動

當(dāng)在子線程中請求完數(shù)據(jù)后,將頁面的刷新添加到主線程的RunLoop的defaultMode中,這樣當(dāng)用戶不滑動頁面 由UITrackingMode切換到DefaultMode的時候進(jìn)行更新UI不會影響滑動
[self performSelectorOnMainThread:...modes:];


AFNetWoring中如何運(yùn)用RunLoop

AFNetWorking中的AFURLConnectionOperatiion是基于NSURLConnection進(jìn)行封裝的。AFNetWoring為了能在后臺接收delegate的回調(diào),創(chuàng)建了一個線程,并在當(dāng)前線程中開啟了循環(huán)的RunLoop
當(dāng)需要這個后臺線程執(zhí)行任務(wù)時 直接調(diào)用[self performSelector: onThread:]將其扔到后臺線程處理

performSelector的實(shí)現(xiàn)原理

調(diào)用performSletor:afterDelay:會在內(nèi)部默認(rèn)創(chuàng)建一個NSTimer.并添加到當(dāng)前線程的RunLoop中,如果當(dāng)前線程沒有開啟RunLoop該方法將會失效
調(diào)用performSelector:onThread:也會默認(rèn)開啟一個NSTimer。

利用RunLoop解釋一下頁面渲染的過程

  • 調(diào)用view 的setNeedsDisplay會默認(rèn)調(diào)用layer的setNeedsDesplay方法。
  • 此時不會立即進(jìn)行繪制。而是給當(dāng)前l(fā)ayer打上一個臟標(biāo)記,當(dāng)RunLoop即將要休眠時,進(jìn)行繪制工作。
  • [調(diào)用CALayer display]方法,CALayer會判斷其delegate是否有實(shí)現(xiàn)異步繪制方法,如果沒有實(shí)現(xiàn)進(jìn)入系統(tǒng)繪制流程。如果實(shí)現(xiàn)了 進(jìn)入異步繪制流程
  • 最后CALayer將位圖提交給Backing Store.最后交給GPU。繪制結(jié)束。

NSNotificationCenter的原理

  • NSNotificationCenter是使用觀察者模式來實(shí)現(xiàn)的用于跨層傳遞消息,用來降低耦合度。
  • NSNotificationCenter用來管理通知,將觀察者注冊到NSNotificationCenter的通知調(diào)度表中,然后發(fā)送通知時利用標(biāo)識符name和object識別出調(diào)度表中的觀察者,然后調(diào)用相應(yīng)的觀察者的方法,即傳遞消息。

其他整理

UIControl只是封裝了target-action模型和控件通用狀態(tài)。target-action的回調(diào)是通過UIApplication轉(zhuǎn)發(fā)的。touch event的處理沒什么特殊的,是UIButton覆蓋了gestureRecognizerShouldBegin, 屏蔽了對應(yīng)UITapGesture的識別

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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