這篇文章主要總結(jié)下runloop的主要組成及實現(xiàn)流程,以及衍生出來的一些相關(guān)問題:
1.組成
2.主要方法(實現(xiàn)流程)
3.線程保活需要添加item
4.GCD和NSTimer的準(zhǔn)確性
1.組成
CFRunLoop類,
CFRunLoopMode類,
CFRunLoopSource類,
CFRunLoopTimer類,
CFRunLoopObserver類
//一個runloop中包含
struct __CFRunLoop {
pthread_t _pthread; //對應(yīng)的線程,runloop和線程是一一對應(yīng)的
CFMutableSetRef _commonModes; //commonMode是針對kCFRunLoopCommonModes,是_modes的子集
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode; //當(dāng)前的mode
CFMutableSetRef _modes; //runloop中所有的mode
};
//mode
struct __CFRunLoopMode {
CFMutableSetRef _sources0; //系統(tǒng)事件
CFMutableSetRef _sources1; //線程通信
CFMutableArrayRef _observers; //監(jiān)聽者
CFMutableArrayRef _timers; //定時器
};
mode類型有
UITrackingRunLoopMode //處理滾動
kCFRunLoopDefaultMode //默認(rèn)模式
UIInitializationRunLoopMode //啟動程序后的過渡mode,啟動完成后就不再使用
GSEventReceiveRunLoopMode //Graphic相關(guān)事件的mode,通常用不到
kCFRunLoopCommonModes //監(jiān)聽commonModes中mode,比如mainRunloop中commonModes包含UITrackingRunLoopMode,kCFRunLoopDefaultMode,所以timer設(shè)置mode = kCFRunLoopCommonModes,就可以同時監(jiān)聽這兩種mode下的活動
2.實現(xiàn)流程
其中runloop主要方法是
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
//判斷mode是否存在,mode中是否有需要執(zhí)行的東西,通過這里可以看出如果需要讓一個線程?;?這里需要開啟runloop之外,還需要有mode和任務(wù)(source,timer,observer...)
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
//通知監(jiān)聽者即將進(jìn)入runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//執(zhí)行runloop
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//通知監(jiān)聽者已經(jīng)退出runloop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
//就是一個do-while循環(huán)
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
02、通知Observers:即將處理Timers
03、通知Observers:即將處理Sources
04、處理Blocks
05、處理Source0(可能會再次處理Blocks)
06、如果存在Source1,就跳轉(zhuǎn)到第8步
07、通知Observers:開始休眠(等待消息喚醒)
...(休眠)
08、通知Observers:結(jié)束休眠(被某個消息喚醒)
01> 處理Timer
02> 處理GCD Async To Main Queue
03> 處理Source1
09、處理Blocks
10、根據(jù)前面的執(zhí)行結(jié)果,決定如何操作
retVal = ...(來決定是否退出循環(huán))
} while (0 == retVal);
return retVal;
}
這個方法run函數(shù)中,最終用來執(zhí)行這些source,timer..的就是下面圖中,右邊黑框中的函數(shù)

WechatIMG19.jpeg
其實我們的函數(shù)調(diào)用大部分都是始于runLoop,比如ViewController中的viewDidLoad,這些方法(方法鏈,我指的是整個函數(shù)調(diào)用棧,什么時候開始的第一個函數(shù)調(diào)用)什么時候調(diào)用,都是runloop來控制的,我們在viewDidLoad中打斷點發(fā)現(xiàn)

WechatIMG18.jpeg
3.線程?;?/h1>
-(void)threadAlive {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (1) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
[thread start];
}
- [[NSRunLoop currentRunLoop] run] 是無法停止的,所以如果不是整個app都需要保活的話要使用上述方案;
- 需要添加任務(wù)到mode中,否則無法開啟runloop循環(huán), 上述CFRunLoopRunSpecific函數(shù)中有明確的代碼;
-(void)threadAlive {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc] init] forMode:NSDefaultRunLoopMode];
while (1) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
[thread start];
}