Runloop
通過內(nèi)部維護(hù)事件循環(huán)來對(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
集合CFMutableSetRef
commonModes
:CFMutableSetRef
commonModeItems
: 集合悬秉,包含多個(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
: 名稱NSDefaultRunLoopMode
source0
:處理點(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)窘游,觀測時(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è)無限的循環(huán)案怯,無法停止。
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可能無法響應(yīng)要怎么解決?
runloop 是怎么響應(yīng)用戶操作的捐韩, 具體流程是什么樣的仅政?
說說runLoop的幾種狀態(tài)
runloop的mode作用是什么?