前言
應(yīng)用在運行以后,只要有觸發(fā)事件(點擊按鈕),應(yīng)用程序就會立刻做出相應(yīng)的反應(yīng),如果不對它進行操作,應(yīng)用程序就像靜止了一樣。給我們的感覺就像應(yīng)用一直處于隨時待命的狀態(tài),在沒人操作的時候它一直在休息,在讓它干活的時候,它就能立刻響應(yīng)。那這種效果在底層是怎樣實現(xiàn)的呢?
RunLoop定義與作用
定義:在程序運行過程中循環(huán)做一些事情。
作用:保持程序的持續(xù)運行;處理App中的各種事件(觸摸事件、定時器事件);節(jié)省CPU資源,提高程序性能:該做事時做事,該休息時休息;
RunLoop與線程
每條線程都有唯一的一個與之對應(yīng)的RunLoop對象;
RunLoop保存在一個全局的Dictionary里,線程作為key,RunLoop作為value;
線程剛創(chuàng)建時并沒有RunLoop對象,RunLoop會在第一次獲取它時創(chuàng)建;
RunLoop會在線程結(jié)束時銷毀;
主線程的RunLoop已經(jīng)自動獲?。▌?chuàng)建),子線程默認沒有開啟RunLoop;
底層結(jié)構(gòu)
一個RunLoop對象(CFRunLoopRef)中包含若干個運行模式(CFRunLoopModeRef)。而每一個運行模式下又包含若干個輸入源(CFRunLoopSourceRef)、定時源(CFRunLoopTimerRef)、觀察者(CFRunLoopObserverRef)。

struct __CFRunLoop {
pthread_t _pthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
};
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
};
CFRunLoopModeRef
CFRunLoopModeRef代表RunLoop的運行模式;
一個RunLoop包含若干個mode,每個mode又包含若干個Source0/Source1/Time/Observer;
RunLoop啟動時只能選擇其中一個Mode,作為currentMode;
如果需要切換Mode,只能切換當(dāng)前的Mode,再重新選擇一個Mode進入;
不同組的Source0/Source1/Timer/Observer能分隔開來,互不影響;
CFRunLoopModeRef
kCFRunLoopDefaultMode(NSDefaultRunLoopMode):App的默認Mode,通常主線程是在這個Mode下運行;
UITrackingRunLoopMode:界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動,保證界面滑動時不受其他 Mode 影響;
kCFRunLoopCommonModes:偽模式,不是一種真正的運行模式;
Source0 : 觸摸事件處理 ,performSelector:onThread:
Source1 : 基于Port的線程間通信 , 系統(tǒng)事件捕捉
Times : NSTimer , performSelector:withObject:afterDelay:
Observers : 用于監(jiān)聽RunLoop的狀態(tài) ; UI刷新(BeforeWaiting) ; Autorelease pool(BeforeWaiting)
源碼分析
創(chuàng)建iOS項目,在- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event方法打斷點,點擊手機屏幕進入斷點,在控制臺輸入bt指令,可以看到RunLoop的調(diào)用流程,RunLoop的入口為:CFRunLoopRunSpecific函數(shù)。

C語言代碼比較難理解,源碼進過刪減如下:
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
//通知觀察者進入RunLoop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
//創(chuàng)建并啟動RunLoop:去處理需要做的事情
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//通知觀察者退出RunLoop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
int32_t retVal = 0;
do {
//通知Observer即將處理Timers
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//通知Observer即將處理Sources
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//處理Block
__CFRunLoopDoBlocks(rl, rlm);
//處理Sources0
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
//處理Block
__CFRunLoopDoBlocks(rl, rlm);
}
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
//判斷有無Sources1
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
//如果有Sources1,就跳轉(zhuǎn)到handle_msg
goto handle_msg;
}
didDispatchPortLastTime = false;
//通知Observer即將處理休眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
//進入休眠
__CFRunLoopSetSleeping(rl);
do {
//循環(huán)等待新的消息來喚醒當(dāng)前線程
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
} while (1);
// user callouts now OK again
__CFRunLoopUnsetSleeping(rl);
//通知Observer結(jié)束休眠
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
//被Timers喚醒,處理Timers事件
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
__CFArmNextTimerInMode(rlm, rl);
}
}
//被Timers喚醒,處理Timers事件
else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
__CFArmNextTimerInMode(rlm, rl);
}
}
//被GCD喚醒,處理Timers事件
else if (livePort == dispatchPort) {
//處理GCD事件
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else {
//被Source1喚醒,處理Source1事件
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
}
//處理Block
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
retVal = kCFRunLoopRunFinished;
}
voucher_mach_msg_revert(voucherState);
os_release(voucherCopy);
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
由源碼__CFRunLoopRun函數(shù)中我們可以發(fā)現(xiàn),RunLoop的本質(zhì)就是一個循環(huán)(do .... while)。