- 概念
- 數(shù)據(jù)結構
- 事件循環(huán)機制
- RunLoop 與 NSTimer
- RunLoop 與多線程
一、概念
- RunLoop是通過內部維護的事件循環(huán)來對事件/消息進行管理的一個對象
事件循環(huán):
- 沒有消息需要處理時,休眠以避免資源占用(用戶態(tài)--> 內核態(tài))
- 有消息需要處理,立刻被喚醒(內核態(tài) --> 用戶態(tài))
系統(tǒng)調用相關指令如開關機等是在內核態(tài)的,程序一般運行在用戶態(tài)的,絕大部分API都是用戶態(tài)的

image
二、數(shù)據(jù)結構
NSRunLoop 是 CFRunLoop 的封裝,提供了面相對象的API,其數(shù)據(jù)結構如下:
- CFRunLoop
- CFRunLoopMode
- Source/Timer/Observer
1、CFRunLoop
包含以下內容:
- pthread --- (RunLoop和線程一一對應)
- currentMode --- CFRunLoopMode
- modes --- NSMutableSet<CFRunLoopMode*>
- commonModes --- NSMutableSet<NSString*>
- commonModeItems --- 也是一個集合,包含多個 Observer(觀察者)、Timer、Source
2、CFRunLoopMode
- name --- 名稱,如 NSDefaultRunLoopMode 等
- sources0 -- 集合類型,無序的
- sources1 --- 集合類型
- observers --- 數(shù)組,有序的
- timers --- 數(shù)組
3、CFRunLoopSource
- source0 --- 需要手動喚醒線程
- source1 --- 具備喚醒線程的能力
4、CFRunLoopTimer
- 基于事件的定時器,和 NSTimer 是 toll-free bridged的。(即可以橋轉換)
5、CFRunLoopObserver
觀測時間點:
- kCFRunLoopEntry
- kCFRunLoopBeforeTimers
- kCFRunLoopBeforeSources
- kCFRunLoopBeforeWaiting --- 即將休眠,即將切換內核狀態(tài)
- kCFRunLoopAfterWaiting --- 內核態(tài)切換到用戶態(tài)不久之后發(fā)出
- kCFRunLoopExit
各個數(shù)據(jù)結構之間的關系
一對多的關系:

image
RunLoop的Mode:
問題:怎樣將Timer同時添加到兩個mode?以保證mode切換的時候,不影響timer的使用

image
解決 :NSRunLoopCommonModes
NSRunLoopCommonModes的特殊性
- commonMode 不是實際存在的一種Mode
- 是同步 Source/Timer/Observer 到多個 Mode 中的一種技術方案
三、事件循環(huán)機制

image
APP運行生命周期內RunLoop的整體事件循環(huán)機制
RunLoop的核心

image
四、RunLoop 與 NSTimer
問題:滑動 TableView 時,定時器還會生效嗎?
回答:不會生效,timer 默認加入到 defaultMode 中,滑動的時候,Mode 會從 kCFRunLoopDefaultMode 切換到 UITrackingRunLoopMode,不同的 Mode ,timer 不相關,所以不生效
解決:添加到 kCFRunLoopCommonMode 中
五、RunLoop 與多線程
- 線程是和 RunLoop 一一對應的
- 自己創(chuàng)建的線程默認是沒有 RunLoop 的
怎樣實現(xiàn)一個常駐線程
- 為當前線程開啟一個 RunLoop
- 向該 RunLoop 中添加一個 Port/Source 等維持 RunLoop 的事件循環(huán)
- 啟動該 RunLoop
代碼實現(xiàn):
運行的模式和資源添加的模式一定要是同一個

image

image
總結
- 什么是 RunLoop ,是怎樣做到有事做事,沒事休息的?
- RunLoop 與線程的關系?
- 如何實現(xiàn)常駐線程?
- 怎樣保證子線程數(shù)據(jù)回來更新UI的時候,不打斷用戶的滑動操作?(把子線程拋給主線程的操作添加到 DefaultMode 下,此時滑動 UITrackingRunLoopMode 不受影響)