
Runloop
通過內(nèi)部維護(hù)事件循環(huán)來(lái)對(duì)事件/消息進(jìn)行管理的一個(gè)對(duì)象。 沒有消息處理時(shí),進(jìn)入休眠以避免資源占用 有消息時(shí),立刻被喚醒
904629-1c7712439e0c4135.png
事件循環(huán)(Event Loop) 事件循環(huán)對(duì)消息進(jìn)行管理,管理狀態(tài)的切換 沒有消息時(shí): 用戶態(tài) ----> 內(nèi)核態(tài) 有消息時(shí):用戶態(tài) <---- 內(nèi)核態(tài)
main函數(shù)為什么不會(huì)退出? 因?yàn)檎{(diào)用UIApplicationMain啟動(dòng)了一個(gè)Runloop。有接收消息進(jìn)行處理,沒有消息進(jìn)入等待。
NSRunLoop是CFRunLoop的封裝,提供面向?qū)ο蟮腁PI。
CFRunLoop分析
查看蘋果源碼,CFRunLoop結(jié)構(gòu)體如下:

pthread: 與線程相關(guān),一一對(duì)應(yīng)。currentMode:當(dāng)前RunLoop所處的模式modes集合CFMutableSetRefcommonModes:CFMutableSetRefcommonModeItems: 集合,包含多個(gè)Observer、Timer、Source。
CFRunLoopMode
Mode代表RunLoop的不同運(yùn)行模式,運(yùn)行時(shí)只能選擇一種模式運(yùn)行作為currentMode,如果想切換需要退出當(dāng)前循環(huán),重新選擇Mode在進(jìn)入循環(huán)。為什么這樣設(shè)計(jì)呢,為了隔離開不同的任務(wù),互不影響。
CFRunLoopMode結(jié)構(gòu)體:

CFRunLoopModeRef
RunLoopMode中包含的內(nèi)容:
name: 名稱NSDefaultRunLoopModesource0:處理點(diǎn)擊事件,performSelector:onThread:withObject:方法。source1:observers數(shù)組timers數(shù)組
一個(gè)Mode對(duì)應(yīng)多個(gè)Source/Timer/Observer。RunLoop只能接受到當(dāng)前Mode中添加的事件。
兩種常用的Mode:
kCFRunLoopDefaultMode(NSDefaultRunLoopMode): APP的默認(rèn)Mode,通常主線程是這個(gè)模式。UITrackingRunLoopMode: 界面跟蹤Mode,用戶ScrollView追蹤觸摸滑動(dòng),保證界面滑動(dòng)是不受其他Mode的影響。
注意:NSRunLoopCommonMode不是實(shí)際存在的Mode。是同步Source/Timer/Observer到多個(gè)Mode中的一種技術(shù)方案。
CFRunLoopSource
source0: 手動(dòng)喚醒線程。 觸摸事件,
performSelector:onThread:withObject:方法。source1 :自動(dòng)喚醒線程,基于Port的線程間通訊 ,系統(tǒng)事件的捕捉。
CFRunLoopTimer
基于事件的定時(shí)器,可以與NSTimer進(jìn)行轉(zhuǎn)換。 performSelector:withObject:afterDelay:
CFRunLoopObserver
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"KCFRunLoopEntry");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"Timers");
break;
case kCFRunLoopBeforeSources:
NSLog(@"Sources");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"BeforeWaiting");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"AfterWaiting");
break;
case kCFRunLoopExit:
NSLog(@"Exit");
break;
default:
break;
}
});
CFRunLoopAddObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes);
CFRelease(observer);
監(jiān)聽RunLoop狀態(tài),觀測(cè)時(shí)間點(diǎn):
kCFRunLoopEntry進(jìn)入RunLoopkCFRunLoopBeforeTimers將要處理Timer事件 *kCFRunLoopBeforeSources將要處理Source事件kCFRunLoopBeforeWaiting將要進(jìn)入休眠狀態(tài),切換用戶態(tài)--->內(nèi)核態(tài)。kCFRunLoopAfterWaiting喚醒,內(nèi)核態(tài)--->用戶態(tài)kCFRunLoopExit退出
自動(dòng)釋放池跟RunLoop的狀態(tài)有關(guān)
一個(gè)RunLoop對(duì)應(yīng)多個(gè)Mode,一個(gè)Mode對(duì)應(yīng)多個(gè)Source/Timer/Observer。
事件循環(huán)的實(shí)現(xiàn)機(jī)制

RunLoop的應(yīng)用
滑動(dòng)TableView的時(shí)候定時(shí)器為什么會(huì)失效?
因?yàn)榛瑒?dòng)TableView的時(shí)候,RunLoop發(fā)生了Mode切換。 kCFRunLoopDefaultMode ---> UITrackingRunLoopMode。 解決方法:將Timer添加到RunLoop使用commonMode標(biāo)記設(shè)置。
// 這種方式添加定時(shí)器不會(huì)出現(xiàn),Timer停止的問題。
static int count = 0;
NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"%d", count ++);
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
// NSRunLoopCommonModes 并不是一個(gè)真的模式,而是一個(gè)標(biāo)記。
如何實(shí)現(xiàn)常駐線程?
經(jīng)常使用子線程時(shí),可以創(chuàng)建一個(gè)常駐子線程,可以節(jié)省系統(tǒng)資源。下面看創(chuàng)建常駐子線程:
BOOL shouldKeepRunning = YES; // global
- (void)viewDidLoad {
[super viewDidLoad];
// LEThread繼承自NSThread重寫dealloc方法,打印銷毀信息。
self.thread = [[LEThread alloc] initWithBlock:^{
// 用于線程?;?,不做具體的任務(wù)。
// 線程一直在運(yùn)行
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
// [[NSRunLoop currentRunLoop] run]; // 開啟一個(gè)無(wú)限的循環(huán),無(wú)法停止。
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
NSLog(@"%s ---end----", __func__);
}];
// 啟動(dòng)子線程
[self.thread start];
}
?
//停止線程
// 停止子線程RunLoop,該方法需要在子線程中調(diào)用。
- (void)stop {
shouldKeepRunning = NO; // 標(biāo)志
CFRunLoopStop(CFRunLoopGetCurrent()); // 釋放當(dāng)前RunLoop。
NSLog(@"%s", __func__);
}
為當(dāng)前線程開啟一個(gè)RunLoop。
向該RunLoop中添加一個(gè)Port/Source等維持RunLoop的事件循環(huán)。
啟動(dòng)該RunLoop。
小結(jié)
講講
RunLoop,項(xiàng)目中有用到嗎?runloop內(nèi)部實(shí)現(xiàn)邏輯?
runloop和線程的關(guān)系?
timer 與 runloop 的關(guān)系?
程序中添加每3秒響應(yīng)一次的NSTimer,當(dāng)拖動(dòng)tableview時(shí)timer可能無(wú)法響應(yīng)要怎么解決?
runloop 是怎么響應(yīng)用戶操作的, 具體流程是什么樣的?
說說runLoop的幾種狀態(tài)
runloop的mode作用是什么?
