Runloop
[TOC]
一覆旱、概念
Runloop就是一個事件處理的循環(huán)缕探,用來不停的調(diào)度工作及處理輸入事件Runloop是事件接收和分發(fā)機制的一個實現(xiàn)找爱。是線程相關的基礎框架的一部分绍坝。
實例:main 函數(shù)(有無循環(huán)對比)
問題:UIApplicationMain做了什么碰辅?
// 偽代碼
int main(int argc, char * argv[]) {
@autoreleasepool {
int retVal = 0;
do {
// 睡眠中等待消息
int message = sleep_and_wait();
// 處理消息
retVal = process_message(retVal);
} while (0 == retVal);
}
return 0;
}
基本作用
- 保持程序的持續(xù)運行采郎。
- 處理app的各種事件千所。如:觸摸事件、定時器事件等蒜埋。
- 節(jié)省CPU資源淫痰,提高程序性能。Runloop并不是由系統(tǒng)自動控制的整份,需要檢測是否有需要處理的事件待错。如果有則去處理,如果沒有則進入睡眠以節(jié)省CPU烈评。
Runloop 是一個對象
iOS中有兩套API訪問Runloop對象:
Foundation框架:NSRunloop
Core Foundation:CFRunloopRef
NSRunloop是基于CFRunloopRef的一層OC包裝火俄,兩者皆是Runloop對象。
Runloop 與線程
- 每個線程都有且僅有一個與之對應的Runloop對象讲冠。
- Runloop 保存在一個全局的 Dictionary 中烛占,線程為 key,線程對應的 Runloop 為 value沟启。
- 線程剛創(chuàng)建的時候是沒有Runloop對象的忆家,Runloop會在第一次獲取時創(chuàng)建(懶加載)。
- 主線程默認創(chuàng)建Runloop德迹,子線程默認不開啟芽卿。
主線程在創(chuàng)建時也是沒有 runloop 對象的,而是 UIApplicationMain 方法里創(chuàng)建胳搞。 - Runloop 在線程結(jié)束時銷毀卸例。
獲取 Runloop 對象
// 獲取當前線程的 Runloop 對象
NSRunLoop *currentRunloop = [NSRunLoop currentRunLoop];
CFRunLoopRef currentRunloopRef = CFRunLoopGetCurrent();
// 獲取主線程的 Runloop 對象
NSRunLoop *mainRunloop = [NSRunLoop mainRunLoop];
CFRunLoopRef mainRunloopRef = CFRunLoopGetMain();
/**
問題:內(nèi)存地址不一致称杨?
NSRunLoop對象 包含 CFRunLoopRef對象。
*/
源碼:CFRunLoop.c
二筷转、API
Core Foundation中關于RunLoop的5個類
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
pthread_t _pthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
};
CFRunLoopModeRef
- CFRunLoopModeRef 代表 Runloop 的運行模式姑原。
- 一個 Runloop 包含多個 Mode,每個 Mode 又包含若干個 Source0/Source1/Timer/Observer呜舒。
不同組的 Source0/Source1/Timer/Observer 能分隔開來锭汛,互不影響。(實例:CommonMode)
如果Mode里沒有任何Source0/Source1/Timer/Observer袭蝗,RunLoop會立馬退出唤殴。 - Runloop 啟動時只能選擇其中一個 Mode,作為 currentMode到腥。
- 如果需要切換 Mode朵逝,只能退出當前 loop,再重新選擇一個 Mode 進入乡范。
目前已知的 Mode有5種
- kCFRunLoopDefaultMode:App的默認Mode配名,通常主線程是在這個Mode下運行。
- UITrackingRunLoopMode:界面跟蹤 Mode晋辆,用于 ScrollView 追蹤觸摸滑動段誊,保證界面滑動時不受其他 Mode 影響。
- UIInitializationRunLoopMode:在剛啟動 App 時第進入的第一個 Mode栈拖,啟動完成后就不再使用连舍。
- GSEventReceiveRunLoopMode:接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到涩哟。
- kCFRunLoopCommonModes:這是一個占位用的Mode索赏,不是一種真正的Mode。
Mode屬性的基本概念
-
Source0:
- 觸摸事件 (Line source0Demo)
- performSelector:onThread:
-
Source1:
- 基于port的線程通信
- 系統(tǒng)事件捕捉
-
Timers:
- NSTimer
- performSelector:withObject:afterDelay:
-
Observers:
- 用于監(jiān)聽 Runloop 的狀態(tài)
- UI刷新(BeforeWaiting)
- Autoreleasetool(BeforeWaiting)
以上即為 Runloop 處理的事件贴彼。
Runloop響應用戶操作:Source1捕捉用戶操作潜腻,分發(fā)給Source0進行處理。
三器仗、Runloop 的運行邏輯
Runloop 狀態(tài)
/* Run Loop Observer Activities */
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 中加入自己的觀察者以便監(jiān)控著 RunLoop 的運行過程融涣。
示例:
- 監(jiān)聽 Runloop 狀態(tài) (Line observeRunloopStatus)
- 創(chuàng)建監(jiān)聽器
- 將監(jiān)聽器加入 Runloop
- 監(jiān)聽回調(diào)
- NSTimer
- 滾動模式與默認模式的切換
Runloop 的運行流程
1. 通知Observers:進入Loop
2. 通知Observers:即將處理Timers
3. 通知Observers:即將處理Sources
4. 處理Blocks
5. 處理Source0(可能會再次處理Blocks)
6. 如果存在Source1,就跳轉(zhuǎn)到第8步
7. 通知Observers:開始休眠(等待消息喚醒)
8. 通知Observers:結(jié)束休眠(被某個消息喚醒)
1. 處理Timer
2. 處理GCD Async To Main Queue
3. 處理Source1
9. 處理Blocks
10. 根據(jù)前面的執(zhí)行結(jié)果精钮,決定如何操作
1. 回到第02步
2. 退出Loop
11. 通知Observers:退出Loop
實例:
1. touch 事件堆棧
2. GCD Async To Main Queue (Line gcdMainQueue)
源碼:CFRunLoopRunSpecific
休眠狀態(tài):內(nèi)核休眠威鹿,CPU不分配資源,線程睡眠狀態(tài)轨香。
四忽你、應用
- 控制線程生命周期(線程保活)(Line source0Demo)
- 解決NSTimer在滑動時停止工作的問題(commonModes)