1. RunLoop本質(zhì)
學(xué)習(xí)鏈接
RunLoop是通過內(nèi)部維護(hù)的事件循環(huán)來對(duì)事件荠卷、消息進(jìn)行管理的一個(gè)對(duì)象
事件循環(huán):沒有消息需要處理時(shí),休眠以避免資源占用科盛。有消息處理時(shí)早处,立刻被喚醒。
休眠時(shí):用戶態(tài)轉(zhuǎn)為內(nèi)核態(tài)
喚醒時(shí):內(nèi)核態(tài)轉(zhuǎn)為用戶態(tài)
2. RunLoop的數(shù)據(jù)結(jié)構(gòu)
CFRunLoop里面包括:
pthread -->一一對(duì)應(yīng)(一個(gè)線程只有一個(gè)block)
currentMode --> CFRunloopMode
modes --> NSMutableSet<CFRunloopMode *>
commonModes --> NSMutableSet<NSString *>
commonModeItems --> 數(shù)組椒丧,包括多個(gè)Oberver壹甥,Timer,Source
CFRunloopModel里面包括:
name -->kCFRunLoopDefaultMode
sources0 --> 需要手動(dòng)喚醒線程
sources1 --> 具備喚醒線程的能力
observers --> NSMutableArray
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即將進(jìn)入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // 即將處理 Timer
kCFRunLoopBeforeSources = (1UL << 2), // 即將處理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), // 即將進(jìn)入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // 剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), // 即將退出Loop
};
timers --> NSMutableArray
系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:
- kCFRunLoopDefaultMode: App的默認(rèn) Mode壶熏,通常主線程是在這個(gè) Mode 下運(yùn)行的句柠。
- UITrackingRunLoopMode: 界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng)棒假,保證界面滑動(dòng)時(shí)不受其他 Mode 影響溯职。
- UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用帽哑。
- GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode谜酒,通常用不到。
- kCFRunLoopCommonModes: 這是一個(gè)占位的 Mode妻枕,沒有實(shí)際作用僻族。
CommonMode的特殊性:
NSRunLoopCommonModes
- CommonMode不是實(shí)際存在的一種 mode
-
是同步Source/Timer/Obervre到多個(gè)mode的一種技術(shù)方案
image
3. RunLoop事件循環(huán)機(jī)制
4. RunLoop與NSTimer
這里有個(gè)概念叫 “CommonModes”:一個(gè) Mode 可以將自己標(biāo)記為”Common”屬性(通過將其 ModeName 添加到 RunLoop 的 “commonModes” 中)。每當(dāng) RunLoop 的內(nèi)容發(fā)生變化時(shí)屡谐,RunLoop 都會(huì)自動(dòng)將 _commonModeItems 里的 Source/Observer/Timer 同步到具有 “Common” 標(biāo)記的所有Mode里述么。
應(yīng)用場(chǎng)景舉例:主線程的 RunLoop 里有兩個(gè)預(yù)置的 Mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。這兩個(gè) Mode 都已經(jīng)被標(biāo)記為”Common”屬性愕掏。DefaultMode 是 App 平時(shí)所處的狀態(tài)度秘,TrackingRunLoopMode 是追蹤 ScrollView 滑動(dòng)時(shí)的狀態(tài)。當(dāng)你創(chuàng)建一個(gè) Timer 并加到 DefaultMode 時(shí)饵撑,Timer 會(huì)得到重復(fù)回調(diào)剑梳,但此時(shí)滑動(dòng)一個(gè)TableView時(shí),RunLoop 會(huì)將 mode 切換為 TrackingRunLoopMode滑潘,這時(shí) Timer 就不會(huì)被回調(diào)垢乙,并且也不會(huì)影響到滑動(dòng)操作。
有時(shí)你需要一個(gè) Timer众羡,在兩個(gè) Mode 中都能得到回調(diào)侨赡,一種辦法就是將這個(gè) Timer 分別加入這兩個(gè) Mode。還有一種方式粱侣,就是將 Timer 加入到頂層的 RunLoop 的 “commonModeItems” 中”突拢”commonModeItems” 被 RunLoop 自動(dòng)更新到所有具有”Common”屬性的 Mode 里去齐婴。
5. RunLoop與多線程和常駐線程
- 線程和RunLoop一一對(duì)用
- 子線程默認(rèn)沒有開啟,需要手動(dòng)獲取稠茂,系統(tǒng)會(huì)自動(dòng)創(chuàng)建
- 實(shí)現(xiàn)一個(gè)常駐線程
- 為當(dāng)前線程開啟一個(gè)RunLoop
- 向該RunLoop添加一個(gè)Port/Source等維護(hù)RunLoop的事件循環(huán)
- 啟動(dòng)該RunLoop
@implementation MCObject
static NSThread *thread = nil;
// 標(biāo)記是否要繼續(xù)事件循環(huán)
static BOOL runAlways = YES;
+ (NSThread *)threadForDispatch{
if (thread == nil) {
@synchronized(self) {
if (thread == nil) {
// 線程的創(chuàng)建
thread = [[NSThread alloc] initWithTarget:self selector:@selector(runRequest) object:nil];
[thread setName:@"com.imooc.thread"];
//啟動(dòng)
[thread start];
}
}
}
return thread;
}
+ (void)runRequest
{
// 創(chuàng)建一個(gè)Source
CFRunLoopSourceContext context = {0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL};
CFRunLoopSourceRef source = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context);
// 創(chuàng)建RunLoop柠偶,同時(shí)向RunLoop的DefaultMode下面添加Source
CFRunLoopAddSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
// 如果可以運(yùn)行
while (runAlways) {
@autoreleasepool {
// 令當(dāng)前RunLoop運(yùn)行在DefaultMode下面
CFRunLoopRunInMode(kCFRunLoopDefaultMode, 1.0e10, true);
}
}
// 某一時(shí)機(jī) 靜態(tài)變量runAlways = NO時(shí) 可以保證跳出RunLoop情妖,線程退出
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), source, kCFRunLoopDefaultMode);
CFRelease(source);
}
@end