什么是RunLoop
RunLoop從字面意思上理解,就是運行循環的意思,它的基本作用就是保持程序的持續運行,處理App中的比如觸摸、定時、Selector等等事件。RunLoop可以節省CPU的資源,提高程序的性能等等作用。
如果沒有RunLoop
1. int main(int agrc,char *argv[]){
2. NSLog(@"execute main function");//程序開始
3. return 0;//程序結束
4. }
如果有了RunLoop
1. int main(int argc,char *argv[]){
2. BOOL running = YES;
3. do{
4. //處理各種任務,處理各種事件
5. while(running);
6. return 0;
7. }}
- 在有了RunLoop的情況下,由于main函數里面啟動了個RunLoop,所以程序并不會馬上推出,保持持續運行狀態
main函數中的RunLoop
1. int main(int agrc,char *argv[]){
2. @autoreleasepool{
3. return UIApplicationMain(argc,argv,nil,NSStringFromClass*([AppDelegate class]));
4. }
5. }
- UIApplicationMain函數內部就啟動了一個RunLoop
- 所以UIApplicationMain函數一直沒有返回,保持了程序的持續運行
- 這個默認啟動的RunLoop是跟主線程相關連的
RunLoop對象
- iOS中有兩套API來訪問和使用RunLoop
- Foundation -> NSRunLoop
- Core Foundation -> CFRunLoopRef
- NSRunLoop和CFRunLoop都代表著RunLoop對象
- NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解RunLoop內部結構,需要多研究CFRunLoopRef層面的API(Core Foundation層面)
- 每一條線程都有唯一的一個與之對應的RunLoop對象
- 主線程的RunLoop已經自動創建好了,子線程的RunLoop需要主動創建
- RunLoop在第一次獲取時創建,在線程結束時銷毀
獲取RunLoop對象
- Fundation
- [NSRunLoop currentRunLoop];//獲取當前線程的RunLoop對象
- [NSRunLoop mainRunLoop];//獲取主線程的RunLoop對象
- Core Foundation
- CFRunLoopGetCurrent();//獲取當前線程的RunLoop對象
- CFRunLoopGetMain();//獲得主線程的RunLoop對象
RunLoop的相關類
- Core Foundation中關于RunLoop的5個類
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef

CFRunLoopModeref
- CFRunLoopModeRef代表RunLoop的運行模式
- 一個RunLoop包含著若干個Mode,每個Mode又包含若干個Source、Timer、Observer
- 每次RunLoop啟動時,只能指定其中一個Mode,這個Mode被稱作CurrentMode
- 如果需要切換Mode,只能退出Loop,在重新制定一個Mode進入
- 這樣做主要是為了分隔開不同組的Source/Timer/Observe,讓其互不影響
- 系統默認注冊了5個Mode
- kCFRunLoopDefaultMode:App的默認Mode,通常主線程是在這個Mode下運行
- UITrackingRunLoopMode: 界面跟蹤Mode,用于ScrollView追蹤觸摸滑動,保證界面滑動時不受其他Mode影響
- UIInitializationRunLoopMode: 在剛啟動App時第一次進入的第一個Mode,啟動完成之后就不再使用
- GSEventReceiveRunLoopMode: 接受系統事件的內部Mode,通常用不到
- kCFRunLoopCommonModes: 這是一個占位用的Mode,不是一種真正的Mode
CFRunLoopSourceRef
- CFRunLoopSourceRef是事件源(輸入源)
- Source0:非基于Port
- Source1:基于Port
CFRunLoopTimerRef
- CFRunLoopTimerRef是基于時間的觸發器
- 基本上說的就是NSTimer
CFRunLoopObserRef
CFRunLoopObserverRef是觀察者,能夠監聽RunLoop的狀態改變
-
可以監聽的時間點有一下幾個
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { kCFRunLoopEntry = (1UL << 0),//即將進入Loop kCFRunLoopBeforeTimers = (1UL << 1),//即將處理Timer kCFRunLoopBeforeSources = (1UL << 2),//即將處理Source kCFRunLoopBeforeWaiting = (1UL << 5),//即將進入休眠 kCFRunLoopAfterWaiting = (1UL << 6),//剛從休眠中喚醒 kCFRunLoopExit = (1UL << 7),//即將退出Loop kCFRunLoopAllActivities = 0x0FFFFFFFU };
RunLoop處理邏輯
官方版

- 每次運行RunLoop,你的線程的RunLoop會自動處理之前未處理的消息,并通知相關的觀察者。具體如下:
- 通知觀察者run loop已經啟動
- 通知觀察者任何即將要開始的定時器
- 通知觀察者任何即將啟動的非基于端口的源
- 啟動任何準備好的非基于端口的源
- 如果基于端口的源準備好并處于等待狀態,立即啟動,并且進入步驟9
- 通知觀察者線程進入休眠
- 將線程置于休眠直到任意下面的事情發生:
- 某一事件到達基于端口的源
- 定時器啟動
- run loop設置的時間已經超時
- run loop被嫻熟喚醒
- 通知觀察者線程將被喚醒
- 處理未處理的事件
- 如果用戶定義的定時器被啟動,處理定時器事件并重啟。進入步驟2
- 如果處理輸入源啟動,傳遞相應的消息
- 如果run loop被顯示喚醒而且時間還沒超過,重啟run loop。進入步驟2
- 通知觀察者run loop結束
網友整理版

RunLoop小節
- RunLoop整理:
- 從字面意思看:運行循環、跑圈,其實它內部就是do-while循環,就這個循環內部不斷地處理各種任務(比如Source、Timer、Observer)。
- 一個線程對應一個RunLoop,主線程的RunLoop默認已經啟動,子現場的RunLoop得手動啟動。
- RunLoop只能選擇一個模式Mode啟動,如果當前Mode中沒有任何Source、Timer、Observer,那么就直接退出RunLoop
- 自動釋放池什么時候釋放?(在RunLoop睡眠之前釋放(kCFRunLoopBeforeWaiting))
- RunLoop如何使用于應用場景
- 讓一個子線程不進入消亡狀態,等待從其它線程發來消息,處理其它事情
- 在子線程中開啟一個定時器
- 在子線程中長期監控一些行為
- 可以控制定時器在哪種模式下運行
- 可以讓某些事件(行為、任務)在特定模式下執行
- 可以添加Observer監聽RunLoop的狀態,比如監聽點擊事件的處理(在所有點擊事件致前做點事情)