什么是RunLoop
- 從字面意思來看:跑圈竿裂、運(yùn)動(dòng)循環(huán)
- 基本用法:保持程序持續(xù)運(yùn)行、處理App中的各種事件(觸摸事件、定時(shí)器事件奢赂、SEL等等)
- 為什么需要它:節(jié)省CPU資源、 提高性能
如果沒有RunLoop
int main (int argc, char *argv[]) {
NSLog(@"execute main function");
return 0;
}
- 如果沒有RunLoop颈走,第三行完成后程序就結(jié)束了
如果有了RunLoop
int main(int argc, char *argv[]) {
BOOL running = YES;
do {
// 執(zhí)行各種任務(wù)膳灶,處理各種事件
// ...
} while (running);
return 0;
}
- 由于main函數(shù)里面啟動(dòng)了RunLoop,所以程序保持持續(xù)運(yùn)行狀態(tài)
main函數(shù)里的RunLoop
int main(int argc, char *argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 在UIApplicationMain函數(shù)內(nèi)部就啟動(dòng)了一個(gè)RunLoop
- 所以UIApplicationMain函數(shù)一直沒有返回立由,保持了程序的持續(xù)運(yùn)行
- 這個(gè)默認(rèn)啟動(dòng)的RunLoop是根主線程相關(guān)聯(lián)的
RunLoop對象
- iOS中有兩套API來訪問和使用RunLoop
- Foundation中的NSRunloop
- Core Foundation中的CFRunLoopRef
NSRunloop和CFRunLoopRef都代表著RunLoop對象
NSRunloop是基于CFRunLoopRef封裝的轧钓, 所以要了解RunLoop的內(nèi)部結(jié)構(gòu),還是要研究CFRunLoopRef(Core Foundation)層面的API
RunLoop與線程
- 每條線程都有位移的一個(gè)與之對應(yīng)的RunLoop對象
- 主線程的RunLoop一經(jīng)自動(dòng)創(chuàng)建好了锐膜,子線程的RunLoop需要主動(dòng)創(chuàng)建
- RunLoop在第一次獲取時(shí)創(chuàng)建毕箍,在線程結(jié)束時(shí)銷毀
獲得RunLoop對象
- Foundation
// 獲得當(dāng)前線程的RunLoop對象
- [NSRunLoop currentRunLoop];
// 獲得主線程的RunLoop對象
- [NSRunLoop mainRunLoop];
- Core Foundation
// 獲得當(dāng)前線程的RunLoop對象
CFRunLoopGetCurrent();
// 獲得主線程的RunLoop對象
CFRunLoopGetMain();
RunLoop的相關(guān)類
-
Core Foundation中關(guān)于RunLoop的5個(gè)類
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopOberverRef
-
CFRunLoopModeRef
CFRunLoopModeRef代表RunLoop的運(yùn)行模式
一個(gè)RunLoop包含若干個(gè)Mode,每個(gè)Mode有包含若干個(gè)Source/Timer/Observe
每次RunLoop啟動(dòng)時(shí)道盏,只能指定其中一個(gè)Mode霉晕,這個(gè)Mode被稱作CurrentMode
如果需要切換Mode,只能退出RunLoop捞奕,在重新指定一個(gè)RunLoop進(jìn)入
這樣做主要是為了分隔開不同組的Source/Timer/Observe牺堰,讓其互不影響
-
系統(tǒng)默認(rèn)注冊了5個(gè)Mode
kCFRunLoopDefaultMode:App的默認(rèn)Mode,通常主線程再是這個(gè)Mode下運(yùn)行的
UITrackingRunLoopMode:界面跟蹤Mode颅围, 用于ScrollView追蹤觸摸滑動(dòng)伟葫,保證界面滑動(dòng)時(shí)不受其他Mode影響
- UIInitializionRunLoopMode:在剛啟動(dòng)App是進(jìn)入的第一個(gè)Mode,啟動(dòng)時(shí)完成后就不再使用
- GSEcentRunLoopMode:接受系統(tǒng)時(shí)間的內(nèi)部Mode院促,通常不會用到
- kCFRunLoopCommonMode:這是一個(gè)占位用的Mode筏养,不是真正的Mode
-
CFRunLoopSourceRef
-
CGRunLoopRef是事件源(輸入源)
以前的分類方法
Port-Based Source
Custom Input Source
Cocoa Perform Selector Source
現(xiàn)在的分類方法
Source0:不是基于Port的
Source1:基于Port的
-
-
CFRunLoopTimerRef
- CFRunLoopTimerRef是基于事件的觸發(fā)器
- 基本上說的就是NSTimer
-
CFRunLoopObserverRef
- CFRunLoopObserverRef是觀察者,能夠監(jiān)聽RunLoop的狀態(tài)改變
可以監(jiān)聽的時(shí)間點(diǎn)有以下幾個(gè)
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) { // 即將進(jìn)入Loop kCFRunLoopEntry =(1UL << 0), // 即將處理Timer kCFRunLoopBeforeTimers =(1UL << 1), // 即將處理Source kCFRunLoopBeforeSource =(1UL << 2), // 即將進(jìn)入休眠 kCFRunLoopBeforeWaiting =(1UL << 5), // 剛從休眠中喚醒 kCFRunLoopAfterWaiting =(1UL << 6), // 即將退出Loop kCFRunLoopExit =(1UL << 7), } ```
RunLoop處理邏輯 - 官方版
RunLoop的事件隊(duì)列
-
每次運(yùn)行RunLoop常拓,你的線程RunLoop會自動(dòng)處理之前未處理的消息渐溶,并通知相關(guān)的觀察者。具體順序如下
1.通知觀察者RunLoop已經(jīng)啟動(dòng)
2.通知觀察者任何將要開始的定時(shí)器
3.通知觀察者任何即將啟動(dòng)的非基于端口的源
4.啟動(dòng)任何準(zhǔn)備好的非基于端口的源
5.如果基于端口的源準(zhǔn)備好并處于等待狀態(tài)弄抬,立即啟動(dòng)茎辐,并進(jìn)入步驟9
6.通知觀察者線程進(jìn)入休眠
-
7.將線程至于休眠狀態(tài)直到任一下面的事件發(fā)生:
- 某一時(shí)間到達(dá)基于端口的源
- 定時(shí)器啟動(dòng)
- RunLoop設(shè)置的事件已經(jīng)超時(shí)
- RunLoop被顯式喚醒
8.通知觀察者線程將被喚醒
-
9.處理未處理的事件
- 如果用戶定義的定時(shí)器啟動(dòng), 處理定時(shí)器事件并重啟RunLoop掂恕,進(jìn)入步驟2
- 如果輸入源啟動(dòng)拖陆,傳遞相應(yīng)的消息
如果RunLoop被顯式喚醒而且事件還沒超時(shí),重啟RunLoop懊亡,進(jìn)入步驟2
10.通知觀察者RunLoop結(jié)束