- 程序的啟動順序
- 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的識別