什么是RunLoop
- 從字面意思看
- 運行循環
- 跑圈
- 基本作用
- 保持程序的持續運行
- 處理App中的各種事件(比如觸摸事件、定時器事件、Selector事件)
- 節省CPU資源,提高程序性能:該做事時做事,該休息時休息
......
如果沒有RunLoop
int main(int argc, char * argv[]) {
NSLog(@"execute main function");
return 0;
}
- 沒有RunLoop的情況下
- 第3行后程序就結束了
如果有了RunLoop
int main(int argc, char * argv[]) {
BOOL running = YES;
do {
// 執行各種任務,處理各種事件
// ......
} while (running);
return 0;
}
- 有RunLoop的情況下
- 由于main函數里面啟動了個RunLoop,所以程序并不會馬上退出,保持持續運行狀態
main函數中的RunLoop
int main(int argc, char * argv[]) {
@autoreleasepool{
return UIApplicationMain(argc,argv,nil,NSStringFromClass([AppDelegate class]));
}
}
- 第14行代碼的UIApplicationMain函數內部就啟動了一個RunLoop
- 所以UIApplicationMain函數一直沒有返回,保持了程序的持續運行
- 這個默認啟動的RunLoop是跟主線程相關聯的
RunLoop對象
iOS中有2套API來訪問和使用RunLoop
Foundation
NSRunLoop
Core Foundation
CFRunLoopRef
NSRunLoop和CFRunLoopRef都代表著RunLoop對象
NSRunLoop是基于CFRunLoopRef的一層OC包裝,所以要了解* RunLoop內部結構,需要多研究CFRunLoopRef層面的API(Core Foundation層面)
RunLoop與線程
- 每條線程都有唯一的一個與之對應的RunLoop對象
- 主線程的RunLoop已經自動創建好了,子線程的RunLoop需要主動創建
- RunLoop在第一次獲取時創建,在線程結束時銷毀
獲得RunLoop對象
Foundation
[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/Observer,讓其互不影響
- 系統默認注冊了5個Mode:
- kCFRunLoopDefaultMode:App的默認Mode,通常主線程是在這個Mode下運行
- UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響
- UIInitializationRunLoopMode: 在剛啟動 App 時第進入的第一個 Mode,啟動完成后就不再使用
- GSEventReceiveRunLoopMode: 接受系統事件的內部 Mode,通常用不到
- kCFRunLoopCommonModes: 這是一個占位用的Mode,不是一種真正的Mode
CFRunLoopSourceRef
- CFRunLoopSourceRef是事件源(輸入源)
- 以前的分法
Port-Based Sources
Custom Input Sources
Cocoa Perform Selector Sources - 現在的分法
Source0:非基于Port的
Source1:基于Port的
CFRunLoopTimerRef
- CFRunLoopTimerRef是基于時間的觸發器
- 基本上說的就是NSTimer
CFRunLoopObserverRef
- CFRunLoopObserverRef是觀察者,能夠監聽RunLoop的狀態改變
- 可以監聽的時間點有以下幾個
RunLoop官方處理邏輯方式
Run Loop的事件隊列
每次運行Run Loop,線程的Run Loop對會自動處理之前未處理的消息,并通知相關的觀察者。具體的順序如下:
1.通知觀察者Run Loop已經啟動
2.通知觀察者任何即將要開始的定時器
3.通知觀察者任何即將要啟動的非基于端口的源
4.啟動任何準備好的非基于端口的源
5.如果基于端口的源準備好并處于等待狀態,立即啟動;并進入步驟9
6.通知觀察者線程進入休眠
7.將線程置于休眠直至下列任一事件發生:
- 某一事件到達基于端口的源
- 定時器的啟動
- Run Loop設置的時間已經超時
- Run Loop被顯示喚醒
8.通知觀察者線程即將被喚醒
9.處理未處理的事件
- 如果用戶定義的定時器啟動,處理定時器事件并重啟Run Loop。再轉入步驟2
- 如果輸入源啟動,傳遞相應的消息
- 如果Run Loop被顯示喚醒而且時間還沒有超時,重啟Run Loop。再轉入步驟2
10.通知觀察者Run Loop結束