蘋果對(duì)runloop的使用
蘋果在AutoreleasePool、手勢(shì)識(shí)別、事件響應(yīng)、UI更新、定時(shí)器、NSObject延時(shí)調(diào)用方法(performSelecter:afterDelay: )等方面都有使用RunLoop。
runLoop 結(jié)構(gòu)

- 一個(gè)thread對(duì)應(yīng)一個(gè)runloop
- Cocoa層的NSRunLoop是對(duì)CF層的CFRunLoop的封裝
- 一個(gè)runloop對(duì)應(yīng)多個(gè)runLoopMode
- 一個(gè)runloop一次只能執(zhí)行一個(gè)runLoopMode,runloop在同一個(gè)時(shí)間只能且必須在一種特定mode下run。要想切換runLoopMode需要停止并退出當(dāng)前RLM重新進(jìn)入新的runLoopMode。
- 一次執(zhí)行一個(gè)mode的好處在于,底層設(shè)計(jì)相對(duì)簡(jiǎn)單,避免不同的mode耦合在一起,代碼相互影響
- 另一個(gè)好處是這樣可以在不同的mode下執(zhí)行不同的代碼,避免上層業(yè)務(wù)代碼相互影響。
- 多個(gè)mode以及mode的切換是iOS app滑動(dòng)順暢的關(guān)鍵。
- 主線程中不同的代碼指定在不同的mode下運(yùn)行可以提高app的流暢度。
- 每個(gè)runLoopMode包括若干個(gè)runLoopSource、若干個(gè)runLoopTimer、若干個(gè)runLoopObserver。
RunLoop結(jié)構(gòu)體定義
// RunLoop的結(jié)構(gòu)體定義
struct __CFRunLoop {
pthread_mutex_t _lock; // 訪問(wèn)mode集合時(shí)用到的鎖
__CFPort _wakeUpPort; // 手動(dòng)喚醒runloop的端口。初始化runloop時(shí)設(shè)置,僅用于CFRunLoopWakeUp,CFRunLoopWakeUp函數(shù)會(huì)向_wakeUpPort發(fā)送一條消息
pthread_t _pthread; // 對(duì)應(yīng)的線程
CFMutableSetRef _commonModes; // 集合,存儲(chǔ)的是字符串,記錄所有標(biāo)記為common的modeName
CFMutableSetRef _commonModeItems; // 存儲(chǔ)所有commonMode的sources、timers、observers
CFRunLoopModeRef _currentMode; // 當(dāng)前modeName
CFMutableSetRef _modes; // 集合,存儲(chǔ)的是CFRunLoopModeRef
struct _block_item *_blocks_head; // 鏈表頭指針,該鏈表保存了所有需要被runloop執(zhí)行的block。外部通過(guò)調(diào)用CFRunLoopPerformBlock函數(shù)來(lái)向鏈表中添加一個(gè)block節(jié)點(diǎn)。runloop會(huì)在CFRunLoopDoBlock時(shí)遍歷該鏈表,逐一執(zhí)行block
struct _block_item *_blocks_tail; // 鏈表尾指針,之所以有尾指針,是為了降低增加block時(shí)的時(shí)間復(fù)雜度
};
RunLoop提供的主要API
以下API主要包括獲取runloop相關(guān)函數(shù)、runloop運(yùn)行相關(guān)函數(shù)、操作source\timer\observer相關(guān)函數(shù)
// 獲取RunLoop
CF_EXPORT CFRunLoopRef CFRunLoopGetCurrent(void);
CF_EXPORT CFRunLoopRef CFRunLoopGetMain(void);
// 添加commonMode
CF_EXPORT void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef mode);
// runloop運(yùn)行相關(guān)
CF_EXPORT void CFRunLoopRun(void);
CF_EXPORT SInt32 CFRunLoopRunInMode(CFStringRef mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);
CF_EXPORT Boolean CFRunLoopIsWaiting(CFRunLoopRef rl);
CF_EXPORT void CFRunLoopWakeUp(CFRunLoopRef rl);
CF_EXPORT void CFRunLoopStop(CFRunLoopRef rl);
// source相關(guān)操作
CF_EXPORT Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
CF_EXPORT void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
CF_EXPORT void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef mode);
CF_EXPORT CFRunLoopSourceRef CFRunLoopSourceCreate(CFAllocatorRef allocator, CFIndex order, CFRunLoopSourceContext *context);
CF_EXPORT void CFRunLoopSourceSignal(CFRunLoopSourceRef source);
// observer相關(guān)操作
CF_EXPORT Boolean CFRunLoopContainsObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);
CF_EXPORT void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);
CF_EXPORT void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef mode);
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreate(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, CFRunLoopObserverCallBack callout, CFRunLoopObserverContext *context);
CF_EXPORT CFRunLoopObserverRef CFRunLoopObserverCreateWithHandler(CFAllocatorRef allocator, CFOptionFlags activities, Boolean repeats, CFIndex order, void (^block) (CFRunLoopObserverRef observer, CFRunLoopActivity activity)) CF_AVAILABLE(10_7, 5_0);
// timer相關(guān)操作
CF_EXPORT Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CF_EXPORT void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CF_EXPORT void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CF_EXPORT CFRunLoopTimerRef CFRunLoopTimerCreate(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, CFRunLoopTimerCallBack callout, CFRunLoopTimerContext *context);
CF_EXPORT CFRunLoopTimerRef CFRunLoopTimerCreateWithHandler(CFAllocatorRef allocator, CFAbsoluteTime fireDate, CFTimeInterval interval, CFOptionFlags flags, CFIndex order, void (^block) (CFRunLoopTimerRef timer)) CF_AVAILABLE(10_7, 5_0);
/* 讓runloop執(zhí)行某個(gè)block
* 本質(zhì)上是把block插入到一個(gè)由runloop維護(hù)的block對(duì)象組成的鏈表中,在runloop運(yùn)行中取出鏈表里被指定在當(dāng)前mode下運(yùn)行的block,逐一執(zhí)行。
*/
CF_EXPORT void CFRunLoopPerformBlock(CFRunLoopRef rl, CFTypeRef mode, void (^block)(void)) CF_AVAILABLE(10_6, 4_0);
RunLoop與線程關(guān)系
do-while 循環(huán)
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
結(jié)論
可以看到,runloop在run起來(lái)后其實(shí)是用一個(gè)do-while循環(huán)實(shí)現(xiàn)的,不同的是,runloop可以做到不需要處理事務(wù)的時(shí)候就sleep,需要的時(shí)候就work。其作用總結(jié)就是:
保持程序的持續(xù)運(yùn)行
處理App中各種事件(觸摸、定時(shí)器、performSelector)
節(jié)省CPU資源、提供程序的性能:該做事做事,該休息休息
與線程的關(guān)系
##### mainRunloop
CFRunLoopRef mainRunloop = CFRunLoopGetMain();
##### CFRunLoopGetMain()函數(shù)內(nèi)部實(shí)現(xiàn)
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
//可以看到傳了一個(gè)主線程參數(shù)進(jìn)入
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
##### _CFRunLoopGet0()函數(shù)內(nèi)部實(shí)現(xiàn)
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);
//建立全局的字典用來(lái)存儲(chǔ) 線程和runloop的關(guān)系
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//通過(guò)__CFRunLoopCreate()函數(shù)創(chuàng)建runloop實(shí)例對(duì)象
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//將mainLoop 和 mainThreadb存入字典
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
//主線程的loop在上面就已經(jīng)創(chuàng)建了,所以如果獲取不到,那肯定是子線程的loop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
//沒(méi)有l(wèi)oop,那就創(chuàng)建子線程的loop對(duì)象
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//子線程和其runloop也會(huì)被存在這個(gè)全局的字典中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFSpinUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
//返回runloop
return loop;
}
結(jié)論
系統(tǒng)會(huì)創(chuàng)建一個(gè)全局的可變字典CFMutableDictionaryRef dict,儲(chǔ)存線程和runloop;
主線程的runloop是默認(rèn)創(chuàng)建的
子線程的runloop默認(rèn)不啟動(dòng),需要主動(dòng)獲取
內(nèi)部的mode-item關(guān)系
__CFRunLoop對(duì)象內(nèi)部結(jié)構(gòu)
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
//關(guān)聯(lián)的線程
pthread_t _pthread;
uint32_t _winthread;
//有好多的Modes、Items
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
//但是只有一個(gè)currentMode
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFTypeRef _counterpart;
};
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
//有好多的事件源 _sources0、_sources1、_observers、_timers
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
結(jié)論
__CFRunLoop對(duì)象內(nèi)部包含多個(gè)modes
而每個(gè)mode中又包含了多個(gè)items
items就是事件源_sources0、_sources1、_observers、_timers...
但是__CFRunLoop對(duì)象同時(shí)只能持有一種mode:_currentMode

runloop有六大事務(wù)item,這些item都是由runloop調(diào)用的
GCD主隊(duì)列:
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() __attribute__((noinline));
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg) {
_dispatch_main_queue_callback_4CF(msg);
asm __volatile__(""); // thwart tail-call optimization
}
observer源:
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {
if (func) {
func(observer, activity, info);
}
asm __volatile__(""); // thwart tail-call optimization
}
timer:
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info) {
if (func) {
func(timer, info);
}
asm __volatile__(""); // thwart tail-call optimization
}
block:
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) {
if (block) {
block();
}
asm __volatile__(""); // thwart tail-call optimization
}
響應(yīng)source0
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info) {
if (perform) {
perform(info);
}
asm __volatile__(""); // thwart tail-call optimization
}
響應(yīng)source1
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
void *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info),
mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply,
#else
void (*perform)(void *),
#endif
void *info) {
if (perform) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
*reply = perform(msg, size, kCFAllocatorSystemDefault, info);
#else
perform(info);
#endif
}
asm __volatile__(""); // thwart tail-call optimization
}
事務(wù)是如何被調(diào)用的
NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"fire in home -- %@",[[NSRunLoop currentRunLoop] currentMode]);
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
- (void)addTimer:(NSTimer *)timer forMode:(NSRunLoopMode)mode
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) {
CFTypeRef item = (CFTypeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
CFRunLoopAddTimer()
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
__CFRunLoopLock(rl);
//判斷當(dāng)前l(fā)oop的mode類型
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
//將timers添加到當(dāng)前rl->_commonModeItems中
CFSetAddValue(rl->_commonModeItems, rlt);
//判斷set是否存在
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
//將這個(gè)timer 提交到當(dāng)前l(fā)oop的上下文中,方便后面使用去取
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {//其他類型也差不多,這里就不再細(xì)說(shuō)
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm) {
if (NULL == rlm->_timers) {
CFArrayCallBacks cb = kCFTypeArrayCallBacks;
cb.equal = NULL;
rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
}
}
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
__CFRunLoopTimerLock(rlt);
if (NULL == rlt->_runLoop) {
rlt->_runLoop = rl;
} else if (rl != rlt->_runLoop) {
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
return;
}
CFSetAddValue(rlt->_rlModes, rlm->_name);
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
__CFRepositionTimerInMode(rlm, rlt, false);
__CFRunLoopTimerFireTSRUnlock();
if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
__CFRunLoopRun() 源碼太長(zhǎng),只展示部分
static int32_t __CFRunLoopRun__CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
uint64_t startTSR = mach_absolute_time();
if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
return kCFRunLoopRunStopped;
}
...
//調(diào)用__CFRunLoopDoTimers()
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
...
}
__CFRunLoopDoTimers()
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR) { /* DOES CALLOUT */
Boolean timerHandled = false;
CFMutableArrayRef timers = NULL;
//遍歷當(dāng)前runloop中modes中的times加到CFMutableArrayRef timers對(duì)象中
for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt)) {
if (rlt->_fireTSR <= limitTSR) {
if (!timers) timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
CFArrayAppendValue(timers, rlt);
}
}
}
//遍歷上面的CFMutableArrayRef timers,拿到具體每個(gè)timer
for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++) {
CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
//調(diào)用timer
Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
timerHandled = timerHandled || did;
}
if (timers) CFRelease(timers);
return timerHandled;
}
__CFRunLoopDoTimer() 部分源碼
static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt) { /* DOES CALLOUT */
Boolean timerHandled = false;
uint64_t oldFireTSR = 0;
/* Fire a timer */
CFRetain(rlt);
__CFRunLoopTimerLock(rlt);
//判斷是否還在超時(shí)時(shí)間內(nèi)
if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl) {
...
//最終調(diào)用`__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__`
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
...
}
...
}
RunLoop和Mode
mode作為runloop和source\timer\observer之間的橋梁。應(yīng)用在啟動(dòng)時(shí)main runloop會(huì)注冊(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,沒(méi)有實(shí)際作用。
個(gè) RunLoop 包含若干個(gè) Mode,每個(gè) Mode 又可以包含若干個(gè) Source/Timer/Observer。每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode就是runloop的 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入。這樣做主要是為了分隔開(kāi)不同組的 Source/Timer/Observer,讓其互不影響。

mode中有一個(gè)特殊的mode叫做commonMode。commonMode并不是一個(gè)真正的mode,而是若干個(gè)被標(biāo)記為commonMode的普通mode。所以commonMode本質(zhì)上是一個(gè)集合,該集合存儲(chǔ)的是mode的名字,也就是字符串,記錄所有被標(biāo)記為common的modeName。當(dāng)我們向commonMode添加source\timer\observer時(shí),本質(zhì)上是遍歷這個(gè)集合中的所有的mode,把item依次添加到每個(gè)被標(biāo)記為common的mode中(_commonModelItems)。每當(dāng)RunLoop的內(nèi)容發(fā)生變化時(shí),RunLoop都會(huì)自動(dòng)將——commonModeItems里的Source/Observer/Timer同步到具有“Common”標(biāo)記的所有Mode的相對(duì)應(yīng)的Source/Observer/Timer里。
在程序啟動(dòng)時(shí),主線程的runloop有兩個(gè)預(yù)置的mode:kCFRunLoopDefaultMode 和 UITrackingRunLoopMode。默認(rèn)情況下是會(huì)處于defaultMode,滑動(dòng)scrollView列表時(shí)runloop會(huì)退出defaultMode轉(zhuǎn)而進(jìn)入trackingMode。所以,有時(shí)候我們加到defaultMode中的timer事件,在滑動(dòng)列表時(shí)是不會(huì)執(zhí)行的。不過(guò),kCFRunLoopDefaultMode 和 UITrackingRunLoopMode這兩個(gè) Mode 都已經(jīng)被添加到runloop的commonMode集合中。也就是說(shuō),主線程的這兩個(gè)預(yù)置mode默認(rèn)已經(jīng)被標(biāo)記為commonMode。想要我們的timer回調(diào)可以在滑動(dòng)列表的時(shí)候依舊執(zhí)行,只需要把timer這個(gè)item添加到commonMode。
另外可以通過(guò)調(diào)用CFRunLoopAddCommonMode 來(lái)添加一個(gè)模式到“common”模式集
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name; // mode名字
Boolean _stopped; // mode的狀態(tài),標(biāo)識(shí)mode是否停止
CFMutableSetRef _sources0; // sources0事件集合
CFMutableSetRef _sources1; // sources1事件集合
CFMutableArrayRef _observers; // 觀察者數(shù)組
CFMutableArrayRef _timers; // 定時(shí)器數(shù)組
CFMutableDictionaryRef _portToV1SourceMap; //字典。key是mach_port_t,value是CFRunLoopSourceRef
__CFPortSet _portSet; // 端口的集合。保存所有需要監(jiān)聽(tīng)的port,比如_wakeUpPort,_timerPort都保存在這個(gè)數(shù)組中
CFIndex _observerMask; // 添加obsever時(shí)設(shè)置_observerMask為observer的_activities(CFRunLoopActivity狀態(tài))
};
在Core Foundation中,針對(duì)Mode的操作,蘋果只開(kāi)放了以下3個(gè)API(cocoa中也有功能一樣的函數(shù),不再列出)
CF_EXPORT CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl); // 返回當(dāng)前運(yùn)行的mode的name
CF_EXPORT CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl); // 返回當(dāng)前RunLoop的所有mode
CF_EXPORT void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef mode); // 向當(dāng)前RunLoop的common modes中添加一個(gè)mode
我們沒(méi)有辦法直接創(chuàng)建一個(gè)CFRunLoopMode對(duì)象,但是我們可以調(diào)用CFRunLoopAddCommonMode傳入一個(gè)字符串向RunLoop中添加一個(gè)commonMode,傳入的字符串即為Mode的名字,Mode對(duì)象應(yīng)該是此時(shí)在RunLoop內(nèi)部創(chuàng)建的。
每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入。
添加commonMode源碼
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) {
if (!CFSetContainsValue(rl->_commonModes, modeName)) {
CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL;
// 把modeName添加到RunLoop的_commonModes
中
CFSetAddValue(rl->_commonModes, modeName);
if (NULL != set) {
// 定義一個(gè)長(zhǎng)度為2的數(shù)組context, 第一個(gè)元素是runloop,第二個(gè)元素是modeName
CFTypeRef context[2] = {rl, modeName};
// 把commonModeItems數(shù)組中的所有Source/Observer/Timer同步到新添加的mode(CFRunLoopModeRef實(shí)例)
// 遍歷set集合中的每一個(gè)元素作為 __CFRunLoopAddItemsToCommonMode 的第一個(gè)參數(shù),context 作為第二個(gè)參數(shù),調(diào)用__CFRunLoopAddItemsToCommonMode
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
CFRelease(set);
}
} else {
}
}
// 添加item到mode的item集合(數(shù)組)中
static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) {
CFTypeRef item = (CFTypeRef)value;
CFRunLoopRef rl = (CFRunLoopRef)(((CFTypeRef *)ctx)[0]);
CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]);
if (CFGetTypeID(item) == CFRunLoopSourceGetTypeID()) {
// item是source就添加到source"集合"中
CFRunLoopAddSource(rl, (CFRunLoopSourceRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopObserverGetTypeID()) {
// item是observer就添加到observer"數(shù)組"中
CFRunLoopAddObserver(rl, (CFRunLoopObserverRef)item, modeName);
} else if (CFGetTypeID(item) == CFRunLoopTimerGetTypeID()) {
// item是timer就添加到timer"數(shù)組"中
CFRunLoopAddTimer(rl, (CFRunLoopTimerRef)item, modeName);
}
}
RunLoop和Source
__CFRunLoopSource也是一個(gè)結(jié)構(gòu)體,其中有一個(gè)union屬性,它包含了version0和version1,也就是Source0(CFRunLoopSourceContext)和Source1(CFRunLoopSourceContext1)。
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order; /* immutable */
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; /* immutable, except invalidation */
CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
} _context;
};
一個(gè)source對(duì)應(yīng)多個(gè)runloop。之所以使用CFMutableBagRef這種集合結(jié)構(gòu)保存runloop而非array或set。主要原因是bag是無(wú)序的且允許重復(fù)(source對(duì)應(yīng)的runloop是一個(gè)集合,說(shuō)明source可以被添加到多個(gè)runloop中)(就像一個(gè)事件可以在不同的線程中被執(zhí)行)
Source0分析
Source0是用來(lái)處理APP內(nèi)部事件、APP自己負(fù)責(zé)管理,比如UIevent。
調(diào)用底層:因?yàn)閟ource0只包含一個(gè)回調(diào)(函數(shù)指針)它并不能主動(dòng)觸發(fā)事件;CFRunLoopSourceSignal(source)將這個(gè)事件標(biāo)記為待處理;CFRunLoopWakeUp來(lái)喚醒runloop,讓他處理事件。首先創(chuàng)建一個(gè)Source0并添加到當(dāng)前的runLoop中,執(zhí)行信號(hào),標(biāo)記待處理CFRunLoopSourceSignal,再喚醒runloop去處理CFRunLoopWakeUp,通過(guò)CFRunLoopRemoveSource來(lái)取消移除源,CFRelease(rlp)。打印結(jié)果會(huì)顯示準(zhǔn)備執(zhí)行和取消了,終止了!!!,如果注釋掉CFRunLoopRemoveSource,則會(huì)打印準(zhǔn)備執(zhí)行和執(zhí)行啦。
Source1分析
Source1被用于通過(guò)內(nèi)核和其他線程相互發(fā)送消息。
__調(diào)用底層:__Source1包含一個(gè) mach_port和一個(gè)回調(diào)(函數(shù)指針)
當(dāng)NSPort對(duì)象接收到端口消息時(shí),會(huì)調(diào)起handlePortMessage
端口接收到消息后會(huì)打印message內(nèi)部屬性:localPort、components、remotePort等
- (void)setupPort{
self.mainThreadPort = [NSPort port];
self.mainThreadPort.delegate = self;
// port - source1 -- runloop
[[NSRunLoop currentRunLoop] addPort:self.mainThreadPort forMode:NSDefaultRunLoopMode];
[self task];
}
- (void) task {
NSThread *thread = [[NSThread alloc] initWithBlock:^{
self.subThreadPort = [NSPort port];
self.subThreadPort.delegate = self;
[[NSRunLoop currentRunLoop] addPort:self.subThreadPort forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
}];
[thread start];
}
- (void)funSendMessage{
NSMutableArray* components = [NSMutableArray array];
NSData* data = [@"hello" dataUsingEncoding:NSUTF8StringEncoding];
[components addObject:data];
[self.subThreadPort sendBeforeDate:[NSDate date] components:components from:self.mainThreadPort reserved:0];
}
- (void)handlePortMessage:(id)message {
NSLog(@"%@", [NSThread currentThread]); // 3 1
unsigned int count = 0;
Ivar *ivars = class_copyIvarList([message class], &count);
for (int i = 0; i<count; i++) {
NSString *name = [NSString stringWithUTF8String:ivar_getName(ivars[i])];
NSLog(@"%@",name);
}
//睡眠一秒后,主線程再向子線程發(fā)送消息。
sleep(1);
if (![[NSThread currentThread] isMainThread]) {
NSMutableArray* components = [NSMutableArray array];
NSData* data = [@"woard" dataUsingEncoding:NSUTF8StringEncoding];
[components addObject:data];
[self.mainThreadPort sendBeforeDate:[NSDate date] components:components from:self.subThreadPort reserved:0];
}
}
Source0和Source1區(qū)別
Source0:source0是App內(nèi)部事件,由App自己管理的,像UIEvent、CFSocket都是source0。source0并不能主動(dòng)觸發(fā)事件,當(dāng)一個(gè)source0事件準(zhǔn)備處理時(shí),要先調(diào)用 CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理。然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來(lái)喚醒 RunLoop,讓其處理這個(gè)事件。框架已經(jīng)幫我們做好了這些調(diào)用,比如網(wǎng)絡(luò)請(qǐng)求的回調(diào)、滑動(dòng)觸摸的回調(diào),我們不需要自己處理。
Source1:由RunLoop和內(nèi)核管理,Mach port驅(qū)動(dòng),如CFMachPort、CFMessagePort。source1包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針),被用于通過(guò)內(nèi)核和其他線程相互發(fā)送消息。這種 Source 能主動(dòng)喚醒 RunLoop 的線程。
添加source的源碼
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) {
// 導(dǎo)出runloop的commonMode(如果modeName是commonMode)
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
// 初始化創(chuàng)建commonModeItems(如果_commonModeItems為空)
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
// 把source添加到commonModeItems集合中
CFSetAddValue(rl->_commonModeItems, rls);
if (NULL != set) {
// 創(chuàng)建一個(gè)長(zhǎng)度為2的數(shù)組,分別存儲(chǔ)runloop和runloopSource
CFTypeRef context[2] = {rl, rls};
// 添加新的item也就是runloopSource到所有的commonMode中
// set是commonMode集合,CFSetApplyFunction遍歷set,添加runloopSource到所有被標(biāo)記為commonMode的mode->source0(或source1)中
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
}
} else {
// 走到這里說(shuō)明modeName不是commonMode
// 根據(jù)modeName和runloop獲取runloop的mode
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
// 初始化創(chuàng)建runloopMode的source0 & source1這個(gè)集合(如果為空)
if (NULL != rlm && NULL == rlm->_sources0) {
rlm->_sources0 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
rlm->_sources1 = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
rlm->_portToV1SourceMap = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, NULL);
}
// 如果runloopMode的sources0集合和sources1都不包含將要添加的runloopSource則把runloopSource添加到對(duì)應(yīng)的集合中
if (NULL != rlm && !CFSetContainsValue(rlm->_sources0, rls) && !CFSetContainsValue(rlm->_sources1, rls)) {
if (0 == rls->_context.version0.version) {
// rls是source0
CFSetAddValue(rlm->_sources0, rls);
} else if (1 == rls->_context.version0.version) {
// rls是source1
CFSetAddValue(rlm->_sources1, rls);
__CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info);
if (CFPORT_NULL != src_port) {
// key是src_port,value是rls,存儲(chǔ)到runloopMode的_portToV1SourceMap字典中
CFDictionarySetValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port, rls);
__CFPortSetInsert(src_port, rlm->_portSet);
}
}
__CFRunLoopSourceLock(rls);
if (NULL == rls->_runLoops) {
// source有一個(gè)集合成員變量runLoops。source每被添加進(jìn)一個(gè)runloop,都會(huì)把runloop添加到他的這個(gè)集合中
// 如官方注釋所言:sources retain run loops!(source會(huì)持有runloop!)
rls->_runLoops = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeBagCallBacks); // sources retain run loops!
}
// 更新runloopSource的runLoops集合,將rl添加到rls->_runloops中
CFBagAddValue(rls->_runLoops, rl);
__CFRunLoopSourceUnlock(rls);
// 如果rls是source0則doVer0Callout標(biāo)記置為true,即需要向外調(diào)用回調(diào)
if (0 == rls->_context.version0.version) {
if (NULL != rls->_context.version0.schedule) {
doVer0Callout = true;
}
}
}
}
// 如果是source0,則向外層(上層)調(diào)用source0的schedule回調(diào)函數(shù)
if (doVer0Callout) {
rls->_context.version0.schedule(rls->_context.version0.info, rl, modeName); /* CALLOUT */
}
}
把source添加到對(duì)應(yīng)mode的source0或source1集合中。只是這里區(qū)分了下source被指定的mode是否為commonMode,如果source被指定的mode是commonMode,還需要把source添加到runloop的commonModeItems集合中
事件響應(yīng)

1.硬件事件(觸摸/鎖屏/搖晃等)
2.IOKit.framework 生成一個(gè) IOHIDEvent 事件并由 SpringBoard 接收(SpringBoard 只接收按鍵(鎖屏/靜音等),觸摸,加速,接近傳感器等幾種 Event)
3.mach port轉(zhuǎn)發(fā)給APP進(jìn)程
4.蘋果注冊(cè)的那個(gè) Source1接收系統(tǒng)事件后在回調(diào) __IOHIDEventSystemClientQueueCallback() 內(nèi)觸發(fā)的 Source0
5.Source0 再觸發(fā)的 _UIApplicationHandleEventQueue()
6._UIApplicationHandleEventQueue() 會(huì)把 IOHIDEvent 處理并包裝成 UIEvent 進(jìn)行處理或分發(fā),其中包括識(shí)別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow
RunLoop和timer
CFRunLoopTimer結(jié)構(gòu)體
struct __CFRunLoopTimer {
uint16_t _bits; // 標(biāo)記fire狀態(tài)
CFRunLoopRef _runLoop; // timer所處的runloop
CFMutableSetRef _rlModes; // mode集合。存放所有包含該timer的mode的modeName,意味著一個(gè)timer可能會(huì)在多個(gè)mode中存在
CFAbsoluteTime _nextFireDate; // 下次觸發(fā)時(shí)間
CFTimeInterval _interval; // 理想時(shí)間間隔(不可變)
CFTimeInterval _tolerance; // 允許的誤差(可變)
CFRunLoopTimerCallBack _callout;// timer回調(diào)
};
和source不同,timer對(duì)應(yīng)的runloop是一個(gè)runloop指針,而非數(shù)組,所以此處說(shuō)明一個(gè)timer只能添加到一個(gè)runloopMode下。
添加timer的源碼
作用:添加timer到rl->commonModeItems中,添加timer到runloopMode中,根據(jù)觸發(fā)時(shí)間調(diào)整timer在runloopMode->timers數(shù)組中的位置。
// 添加timer到runloopMode中,添加timer到rl->commonModeItems中
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
// 導(dǎo)出runloop的commonMode(如果modeName是commonMode)
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
// 如果rl->_commonModeItems為空就初始化rl->commonModeItems
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
// 長(zhǎng)度為2的數(shù)組,分別存放rl和rlt
CFTypeRef context[2] = {rl, rlt};
// 添加新的item也就是timer到所有的commonMode中
// set是commonMode集合,CFSetApplyFunction遍歷set,添加context[1]存放的rlt添加到所有被標(biāo)記為commonMode的mode中
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
// 走到這里說(shuō)明modeName不是commonMode
// 根據(jù)runloop和modeName查找對(duì)用的mode
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm) {
if (NULL == rlm->_timers) {
CFArrayCallBacks cb = kCFTypeArrayCallBacks;
cb.equal = NULL;
rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
}
}
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
__CFRunLoopTimerLock(rlt);
if (NULL == rlt->_runLoop) {
rlt->_runLoop = rl;
} else if (rl != rlt->_runLoop) {
return;
}
// 更新rlt的rlModes集合。將rlm->name添加到name中
CFSetAddValue(rlt->_rlModes, rlm->_name);
// Reposition釋義復(fù)位。所以顧名思義該函數(shù)用于復(fù)位timer
// 此處調(diào)用該函數(shù)本質(zhì)上是按照timer下次觸發(fā)時(shí)間長(zhǎng)短,計(jì)算timer需要插入到runloopMode->timers數(shù)組中的位置,然后把timer插入到runloopMode->timers數(shù)組中
__CFRepositionTimerInMode(rlm, rlt, false);
__CFRunLoopTimerFireTSRUnlock();
// 為了向后兼容,如果系統(tǒng)版本低于CFSystemVersionLion且timer執(zhí)行的rl不是當(dāng)前runloop,則喚醒rl
if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
}
}
}
}
設(shè)置timer下次觸發(fā)時(shí)間的源碼
void CFRunLoopTimerSetNextFireDate(CFRunLoopTimerRef rlt, CFAbsoluteTime fireDate) {
// 觸發(fā)日期大于最大限制時(shí)間,則把觸發(fā)日期調(diào)整為最大觸發(fā)時(shí)間
if (TIMER_DATE_LIMIT < fireDate) fireDate = TIMER_DATE_LIMIT;
uint64_t nextFireTSR = 0ULL;
uint64_t now2 = mach_absolute_time();
CFAbsoluteTime now1 = CFAbsoluteTimeGetCurrent();
// 下次觸發(fā)時(shí)間小于現(xiàn)在則立即觸發(fā)
if (fireDate < now1) {
nextFireTSR = now2;
// 下次觸發(fā)時(shí)間間隔大于允許的最大間隔TIMER_INTERVAL_LIMIT,則將下次觸發(fā)時(shí)間調(diào)整為now + TIMER_INTERVAL_LIMIT
} else if (TIMER_INTERVAL_LIMIT < fireDate - now1) {
nextFireTSR = now2 + __CFTimeIntervalToTSR(TIMER_INTERVAL_LIMIT);
} else {
nextFireTSR = now2 + __CFTimeIntervalToTSR(fireDate - now1);
}
__CFRunLoopTimerLock(rlt);
if (NULL != rlt->_runLoop) {
// 獲取runloopMode個(gè)數(shù)
CFIndex cnt = CFSetGetCount(rlt->_rlModes);
// 聲明名為modes的棧結(jié)構(gòu)
STACK_BUFFER_DECL(CFTypeRef, modes, cnt);
// rlt->rlModes賦值給modes棧結(jié)構(gòu)
CFSetGetValues(rlt->_rlModes, (const void **)modes);
for (CFIndex idx = 0; idx < cnt; idx++) {
// 先retain
CFRetain(modes[idx]);
}
CFRunLoopRef rl = (CFRunLoopRef)CFRetain(rlt->_runLoop);
// 把modes集合中存儲(chǔ)的modeName轉(zhuǎn)換為mode結(jié)構(gòu)體實(shí)例,然后再存入modes集合
for (CFIndex idx = 0; idx < cnt; idx++) {
CFStringRef name = (CFStringRef)modes[idx];
modes[idx] = __CFRunLoopFindMode(rl, name, false);
// 后release
CFRelease(name);
}
// 把上面計(jì)算好的下次觸發(fā)時(shí)間設(shè)置給rlt
rlt->_fireTSR = nextFireTSR;
rlt->_nextFireDate = fireDate;
for (CFIndex idx = 0; idx < cnt; idx++) {
CFRunLoopModeRef rlm = (CFRunLoopModeRef)modes[idx];
if (rlm) {
// Reposition釋義復(fù)位。所以顧名思義該函數(shù)用于復(fù)位timer,所謂復(fù)位,就是調(diào)整timer在runloopMode->timers數(shù)組中的位置
// 此處調(diào)用該函數(shù)本質(zhì)上是先移除timer,然后按照timer下次觸發(fā)時(shí)間長(zhǎng)短計(jì)算timer需要插入到runloopMode->timers數(shù)組中的位置,最后把timer插入到runloopMode->timers數(shù)組中
__CFRepositionTimerInMode(rlm, rlt, true);
}
}
// 以上注釋的意思是:這行代碼的是為了給timer設(shè)置date,但不直接作用于runloop
// 以防萬(wàn)一,我們手動(dòng)喚醒runloop,盡管有可能這個(gè)代價(jià)是高昂的
// 另一方面,這么做的目的也是為了兼容timer的之前的實(shí)現(xiàn)方式
// 如果timer執(zhí)行的rl不是當(dāng)前的runloop,則手動(dòng)喚醒
if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
} else {
// 走到這里說(shuō)明timer的rl還是空,所以只是簡(jiǎn)單的設(shè)置timer的下次觸發(fā)時(shí)間
rlt->_fireTSR = nextFireTSR;
rlt->_nextFireDate = fireDate;
}
}

RunLoop的定時(shí)源,與Source1(Port)一樣,都屬于端口事件源,但不同的是,每一個(gè)Source1都有與之對(duì)應(yīng)的端口,而一個(gè)RunLoopMode中的所有CFRunLoopTimer共用一個(gè)端口
解決定時(shí)器不準(zhǔn)的問(wèn)題
原因:NSTime和CADisplayLink底層都是基于RunLoop的CFRunLoopTimerRef的實(shí)現(xiàn)的,也就是說(shuō)它們都依賴于RunLoop。如果RunLoop的任務(wù)過(guò)于繁重,會(huì)導(dǎo)致它們不準(zhǔn)時(shí)。使用 GCD 的定時(shí)器。GCD 的定時(shí)器是直接跟系統(tǒng)內(nèi)核掛鉤的,而且它不依賴于RunLoop,所以它非常的準(zhǔn)時(shí)
RunLoop 和observer
Observer顧名思義,觀察者,和我們?cè)O(shè)計(jì)模式中的觀察者模式如出一轍。每個(gè) Observer 都包含了一個(gè)回調(diào)(函數(shù)指針),observer主要觀察runloop的狀態(tài)變化,然后執(zhí)行回調(diào)函數(shù)。runloop可觀察的狀態(tài)主要有6種狀態(tài),如下:
// runloop的6種狀態(tài),用于通知observer runloop的狀態(tài)變化
/* Run Loop Observer Activities */
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), // 剛從休眠中喚醒 但是還沒(méi)開(kāi)始處理事件
kCFRunLoopExit = (1UL << 7), // 即將退出Loop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
CFRunLoopObserver結(jié)構(gòu)體定義
struct __CFRunLoopObserver {
CFRunLoopRef _runLoop; // observer所觀察的runloop
CFOptionFlags _activities; // CFOptionFlags是UInt類型的別名,_activities用來(lái)說(shuō)明要觀察runloop的哪些狀態(tài)。一旦指定了就不可變。
CFRunLoopObserverCallBack _callout; // 觀察到runloop狀態(tài)變化后的回調(diào)(不可變)
};
和source不同,observer對(duì)應(yīng)的runloop是一個(gè)runloop指針,而非數(shù)組,此處說(shuō)明一個(gè)observer只能觀察一個(gè)runloop,所以observer只能添加到一個(gè)runloop的一個(gè)或者多個(gè)mode中。
添加Observer源碼
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef rlo, CFStringRef modeName) {
if (modeName == kCFRunLoopCommonModes) {
// 導(dǎo)出runloop的commonModes
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
// 初始化創(chuàng)建commonModeItems
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
// 添加observer到commonModeItems
CFSetAddValue(rl->_commonModeItems, rlo);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlo};
// 添加observer到所有被標(biāo)記為commonMode的mode中
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
}
} else {
rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm && NULL == rlm->_observers) {
rlm->_observers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
}
if (NULL != rlm && !CFArrayContainsValue(rlm->_observers, CFRangeMake(0, CFArrayGetCount(rlm->_observers)), rlo)) {
Boolean inserted = false;
for (CFIndex idx = CFArrayGetCount(rlm->_observers); idx--; ) {
CFRunLoopObserverRef obs = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
if (obs->_order <= rlo->_order) {
CFArrayInsertValueAtIndex(rlm->_observers, idx + 1, rlo);
inserted = true;
break;
}
}
if (!inserted) {
CFArrayInsertValueAtIndex(rlm->_observers, 0, rlo);
}
// 設(shè)置runloopMode的_observerMask為觀察者的_activities(CFRunLoopActivity狀態(tài))
rlm->_observerMask |= rlo->_activities;
__CFRunLoopObserverSchedule(rlo, rl, rlm);
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
自定義Observer來(lái)監(jiān)聽(tīng)runloop狀態(tài)變化
/* 創(chuàng)建一個(gè)observer對(duì)象
第一個(gè)參數(shù): 告訴系統(tǒng)如何給Observer對(duì)象分配存儲(chǔ)空間
第二個(gè)參數(shù): 需要監(jiān)聽(tīng)的狀態(tài)類型
第三個(gè)參數(shù): 是否需要重復(fù)監(jiān)聽(tīng)
第四個(gè)參數(shù): 優(yōu)先級(jí)
第五個(gè)參數(shù): 監(jiān)聽(tīng)到對(duì)應(yīng)的狀態(tài)之后的回調(diào)
*/
CFRunLoopObserverRef observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
switch (activity) {
case kCFRunLoopEntry:
NSLog(@"即將進(jìn)入RunLoop");
break;
case kCFRunLoopBeforeTimers:
NSLog(@"即將處理timer");
break;
case kCFRunLoopBeforeSources:
NSLog(@"即將處理source");
break;
case kCFRunLoopBeforeWaiting:
NSLog(@"即將進(jìn)入休眠");
break;
case kCFRunLoopAfterWaiting:
NSLog(@"從休眠中被喚醒");
break;
case kCFRunLoopExit:
NSLog(@"即將退出RunLoop");
break;
default:
break;
}
});
/* 給主線程的RunLoop添加observer用于監(jiān)聽(tīng)runLoop狀態(tài)
第一個(gè)參數(shù):需要監(jiān)聽(tīng)的RunLoop對(duì)象
第二個(gè)參數(shù):給指定的RunLoop對(duì)象添加的監(jiān)聽(tīng)對(duì)象
第三個(gè)參數(shù):在那種模式下監(jiān)聽(tīng)
*/
CFRunLoopAddObserver(CFRunLoopGetMain(), observer, kCFRunLoopCommonModes);
runloop 更新UI時(shí)機(jī)

runloop如何工作的

USE_DISPATCH_SOURCE_FOR_TIMERS 這個(gè)宏的值為 1,也就是說(shuō)有使用 GCD 來(lái)實(shí)現(xiàn) timer,當(dāng)然 USE_MK_TIMER_TOO 這個(gè)宏的值也是 1,表示也使用了更底層的 timer。

// 一個(gè)do...while循環(huán) 如果不是stop或finish就不斷的循環(huán) 還可以重新啟動(dòng)runloop
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
CFRunLoopRunInMode源碼
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
CFRunLoopRunSpecific源碼
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 如果runloop正在銷毀則直接返回finish
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
// 根據(jù)指定的modeName獲取指定的mode,也就是將要運(yùn)行的mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
// 出現(xiàn)以下情況就不會(huì)return finish:
// 1>.將要運(yùn)行的mode不為空
// 以下這幾條是在__CFRunLoopModeIsEmpty函數(shù)中判斷的:
// 2>.將要運(yùn)行的currentMode是source0、source1、timer任一個(gè)不為空
// 3>.待執(zhí)行的block的mode和將要運(yùn)行的mode相同
// 4>.待執(zhí)行的block的mode是commonMode且待運(yùn)行的mode包含在commonMode中
// 5>.待執(zhí)行的block的mode包含待運(yùn)行的mode
// 6>.待執(zhí)行的block的mode包含commonMode且待運(yùn)行的mode包含在commonMode中
// 所謂待執(zhí)行的block是外部(開(kāi)發(fā)者)通過(guò)調(diào)用CFRunLoopPerformBlock函數(shù)添加到runloop中的
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
return kCFRunLoopRunFinished;
}
volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
int32_t result = kCFRunLoopRunFinished;
// 1.通知observer即將進(jìn)入runloop
// 這里使用currentMode->_observerMask 和 kCFRunLoopEntry按位與操作
// 如果按位與的結(jié)果不是0則說(shuō)明即將進(jìn)入runloop
// 而currentMode->_observerMask是個(gè)什么東西呢?
// currentMode->_observerMask本質(zhì)上是Int類型的變量,標(biāo)識(shí)當(dāng)前mode的CFRunLoopActivity狀態(tài)
// 那么currentMode->_observerMask是在哪里賦值的呢?
// 調(diào)用CFRunLoopAddObserver函數(shù)向runloop添加observer的時(shí)候會(huì)把observer的activities按位或賦值給mode->_observerMask
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// RunLoop的運(yùn)行的最核心函數(shù)
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 10.通知observer即將退出runloop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
rl->_currentMode = previousMode;
return result;
}
/**RunLoop的運(yùn)行的最核心函數(shù)(進(jìn)入和退出時(shí)runloop和runloopMode都會(huì)被加鎖)
* rl: 運(yùn)行的runloop
* rlm: runloop Mode
* seconds: runloop超時(shí)時(shí)間
* stopAfterHandle: 處理完時(shí)間后runloop是否stop,默認(rèn)為false
* previousMode: runloop上次運(yùn)行的mode
*/
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
// 獲取基于系統(tǒng)啟動(dòng)后的時(shí)鐘"嘀嗒"數(shù),其單位是納秒
uint64_t startTSR = mach_absolute_time();
// 狀態(tài)判斷
if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
return kCFRunLoopRunStopped;
}
// 獲取主線程接收消息的port備用。如果runLoop是mainRunLoop且后續(xù)內(nèi)核喚醒的port等于主線程接收消息的port,主線程就處理這個(gè)消息
mach_port_name_t dispatchPort = MACH_PORT_NULL;
Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
#if USE_DISPATCH_SOURCE_FOR_TIMERS
// 初始化獲取timer的port(source1)
// 如果這個(gè)port和mach_msg發(fā)消息的livePort相等則說(shuō)明timer時(shí)間到了,處理timer
mach_port_name_t modeQueuePort = MACH_PORT_NULL;
if (rlm->_queue) {
modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
}
#endif
// 使用GCD實(shí)現(xiàn)runloop超時(shí)功能
dispatch_source_t timeout_timer = NULL;
struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
// seconds是設(shè)置的runloop超時(shí)時(shí)間,一般為1.0e10,11.574萬(wàn)年,所以不會(huì)超時(shí)
if (seconds <= 0.0) { // instant timeout
seconds = 0.0;
timeout_context->termTSR = 0ULL;
} else if (seconds <= TIMER_INTERVAL_LIMIT) {
dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_retain(timeout_timer);
timeout_context->ds = timeout_timer;
timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
// 設(shè)置超時(shí)的時(shí)間點(diǎn)(從現(xiàn)在開(kāi)始 + 允許運(yùn)行的時(shí)長(zhǎng))
timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
dispatch_resume(timeout_timer);
} else { // infinite timeout
seconds = 9999999999.0;
timeout_context->termTSR = UINT64_MAX;
}
Boolean didDispatchPortLastTime = true;
// returnValue 標(biāo)識(shí)runloop狀態(tài),如果returnValue不為0就不退出。
// returnValue可能的值:
// enum {
// kCFRunLoopRunFinished = 1,
// kCFRunLoopRunStopped = 2,
// kCFRunLoopRunTimedOut = 3,
// kCFRunLoopRunHandledSource = 4
// };
int32_t retVal = 0;
do {
voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
voucher_t voucherCopy = NULL;
// 消息緩沖區(qū),用戶緩存內(nèi)核發(fā)的消息
uint8_t msg_buffer[3 * 1024];
// 消息緩沖區(qū)指針,用于指向msg_buffer
mach_msg_header_t *msg = NULL;
// 用于保存被內(nèi)核喚醒的端口(調(diào)用mach_msg函數(shù)時(shí)會(huì)把livePort地址傳進(jìn)去供內(nèi)核寫數(shù)據(jù))
mach_port_t livePort = MACH_PORT_NULL;
__CFPortSet waitSet = rlm->_portSet;
__CFRunLoopUnsetIgnoreWakeUps(rl);
// 2. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)。
// __CFRunLoopDoObservers內(nèi)部會(huì)調(diào)用__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__這個(gè)函數(shù),這個(gè)函數(shù)的參數(shù)包括observer的回調(diào)函數(shù)、observer、runloop狀態(tài)
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 3. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 執(zhí)行被加入的block
// 外部通過(guò)調(diào)用CFRunLoopPerformBlock函數(shù)向當(dāng)前runloop增加block。新增加的block保存咋runloop.blocks_head鏈表里。
// __CFRunLoopDoBlocks會(huì)遍歷鏈表取出每一個(gè)block,如果block被指定執(zhí)行的mode和當(dāng)前的mode一致,則調(diào)用__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__執(zhí)行之
__CFRunLoopDoBlocks(rl, rlm);
// 4. RunLoop 觸發(fā) Source0 (非port) 回調(diào)
// __CFRunLoopDoSources0函數(shù)內(nèi)部會(huì)調(diào)用__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__函數(shù)
// __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__函數(shù)會(huì)調(diào)用source0的perform回調(diào)函數(shù),即rls->context.version0.perform
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
// 如果rl處理了source0事件,那再處理source0之后的block
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
// 標(biāo)記是否需要輪詢,如果處理了source0則輪詢,否則休眠
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
msg = (mach_msg_header_t *)msg_buffer;
// 5. 如果有 Source1 (基于port的source) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)到第9步去處理消息。
// __CFRunLoopServiceMachPort函數(shù)內(nèi)部調(diào)用了mach_msg,mach_msg函數(shù)會(huì)監(jiān)聽(tīng)內(nèi)核給端口發(fā)送的消息
// 如果mach_msg監(jiān)聽(tīng)到消息就會(huì)執(zhí)行g(shù)oto跳轉(zhuǎn)去處理這個(gè)消息
// 第五個(gè)參數(shù)為0代表不休眠立即返回
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
}
didDispatchPortLastTime = false;
// 6. 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)。
// 根據(jù)上面第4步是否處理過(guò)source0,來(lái)判斷如果也沒(méi)有source1消息的時(shí)候是否讓線程進(jìn)入睡眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
// runloop置為休眠狀態(tài)
__CFRunLoopSetSleeping(rl);
// 通知進(jìn)入休眠狀態(tài)后,不要做任何用戶級(jí)回調(diào)
__CFPortSetInsert(dispatchPort, waitSet);
// 標(biāo)記休眠開(kāi)始時(shí)間
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
#if USE_DISPATCH_SOURCE_FOR_TIMERS
do {
msg = (mach_msg_header_t *)msg_buffer;
// 7. __CFRunLoopServiceMachPort內(nèi)部調(diào)用mach_msg函數(shù)等待接受mach_port的消息。隨即線程將進(jìn)入休眠,等待被喚醒。 以下事件會(huì)會(huì)喚醒runloop:
// mach_msg接收到來(lái)自內(nèi)核的消息。本質(zhì)上是內(nèi)核向我們的port發(fā)送了一條消息。即收到一個(gè)基于port的Source事件(source1)。
// 一個(gè)timer的時(shí)間到了(處理timer)
// RunLoop自身的超時(shí)時(shí)間到了(幾乎不可能)
// 被其他調(diào)用者手動(dòng)喚醒(source0)
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
// Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
if (rlm->_timerFired) {
// Leave livePort as the queue port, and service timers below
rlm->_timerFired = false;
break;
} else {
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
// Go ahead and leave the inner loop.
break;
}
} while (1);
#else
msg = (mach_msg_header_t *)msg_buffer;
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
#endif
// 計(jì)算線程沉睡的時(shí)長(zhǎng)
rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopSetIgnoreWakeUps(rl);
// runloop置為喚醒狀態(tài)
__CFRunLoopUnsetSleeping(rl);
// 8. 通知 Observers: RunLoop對(duì)應(yīng)的線程剛被喚醒。
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
// 9. 收到&處理source1消息(第5步的goto會(huì)到達(dá)這里開(kāi)始處理source1)
handle_msg:;
// 忽略端口喚醒runloop,避免在處理source1時(shí)通過(guò)其他線程或進(jìn)程喚醒runloop(保證線程安全)
__CFRunLoopSetIgnoreWakeUps(rl);
if (MACH_PORT_NULL == livePort) {
// livePort為null則什么也不做
CFRUNLOOP_WAKEUP_FOR_NOTHING();
// handle nothing
} else if (livePort == rl->_wakeUpPort) {
// livePort為wakeUpPort則只需要簡(jiǎn)單的喚醒runloop(rl->_wakeUpPort是專門用來(lái)喚醒runloop的)
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
}
#if USE_DISPATCH_SOURCE_FOR_TIMERS
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
// 9.1 如果一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)
// __CFRunLoopDoTimers返回值代表是否處理了這個(gè)timer
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
#if USE_MK_TIMER_TOO
else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
else if (livePort == dispatchPort) {
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
/// 9.2 如果有dispatch到main_queue的block,執(zhí)行block(也就是處理GCD通過(guò)port提交到主線程的事件)。
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
sourceHandledThisLoop = true;
didDispatchPortLastTime = true;
} else {
CFRUNLOOP_WAKEUP_FOR_SOURCE();
/// 9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了,處理這個(gè)事件
// 根據(jù)livePort獲取source(不需要name,從mode->_portToV1SourceMap字典中以port作為key即可取到source)
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
if (rls) {
mach_msg_header_t *reply = NULL;
// 處理source1事件(觸發(fā)source1的回調(diào))
// runloop 觸發(fā)source1的回調(diào),__CFRunLoopDoSource1內(nèi)部會(huì)調(diào)用__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
// 如果__CFRunLoopDoSource1響應(yīng)的數(shù)據(jù)reply不為空則通過(guò)mach_msg 再send給內(nèi)核
if (NULL != reply) {
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
}
}
}
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
/// 執(zhí)行加入到Loop的block
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
/// 進(jìn)入loop時(shí)參數(shù)說(shuō)處理完事件就返回。
retVal = kCFRunLoopRunHandledSource; // 4
} else if (timeout_context->termTSR < mach_absolute_time()) {
/// 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間了
retVal = kCFRunLoopRunTimedOut; // 3
} else if (__CFRunLoopIsStopped(rl)) {
/// 被外部調(diào)用者強(qiáng)制停止了
__CFRunLoopUnsetStopped(rl); // 2
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
// 調(diào)用了_CFRunLoopStopMode將mode停止了
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped; // 2
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
// source/timer/observer一個(gè)都沒(méi)有了
retVal = kCFRunLoopRunFinished; // 1
}
// 如果retVal不是0,即未超時(shí),mode不是空,loop也沒(méi)被停止,那繼續(xù)loop
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
__CFRunLoopRun部分源碼,do-while循環(huán),先初始化一個(gè)存放內(nèi)核消息的緩沖池,獲取所有需要監(jiān)聽(tīng)的port,設(shè)置RunLoop為可以被喚醒狀態(tài),判斷是否有timer、source0、source1回調(diào)。如果有timer則通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)。如果有source0則通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào),執(zhí)行被加入的block。RunLoop 觸發(fā) Source0 (非port) 回調(diào),再執(zhí)行被加入的block。如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息。例如一個(gè)Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)。處理完后再次進(jìn)入__CFArmNextTimerInMode查看是否有其他的timer。如果沒(méi)有事務(wù)需要處理則通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep),此時(shí)會(huì)進(jìn)入一個(gè)內(nèi)循環(huán),線程進(jìn)入休眠狀態(tài)mach_msg_trap(比如我們?cè)跀帱c(diǎn)調(diào)試的時(shí)候),直到收到新消息才跳出該循環(huán),繼續(xù)執(zhí)行run loop。比如監(jiān)聽(tīng)到了事務(wù)基于 port 的Source 的事件、Timer 到時(shí)間了、RunLoop 自身的超時(shí)時(shí)間到了或者被其他什么調(diào)用者手動(dòng)喚醒則喚醒。

CFRunLoopPerformBlock在上圖中作為喚醒機(jī)制有所體現(xiàn),但事實(shí)上執(zhí)行CFRunLoopPerformBlock只是入隊(duì),下次RunLoop運(yùn)行才會(huì)執(zhí)行,而如果需要立即執(zhí)行則必須調(diào)用CFRunLoopWakeUp。
以下是啟動(dòng) run loop 后比較關(guān)鍵的運(yùn)行步驟:
- 通知 observers: kCFRunLoopEntry, 進(jìn)入 run loop
- 通知 observers: kCFRunLoopBeforeTimers, 即將處理 timers
- 通知 observers: kCFRunLoopBeforeSources, 即將處理 sources
- 處理 blocks, 可以對(duì) CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK 函數(shù)下斷點(diǎn)觀察到
- 處理 sources 0, 可以對(duì) CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION 函數(shù)下斷點(diǎn)觀察到
- 如果第 5 步實(shí)際處理了 sources 0,再一次處理 blocks
- 如果在主線程,檢查是否有 GCD 事件需要處理,有的話,跳轉(zhuǎn)到第 11 步
- 通知 observers: kCFRunLoopBeforeWaiting, 即將進(jìn)入等待(睡眠)
- 等待被喚醒,可以被 sources 1、timers、CFRunLoopWakeUp 函數(shù)和 GCD 事件(如果在主線程)
- 通知 observers: kCFRunLoopAfterWaiting, 即停止等待(被喚醒)
- 被什么喚醒就處理什么:
- 被 timers 喚醒,處理 timers,可以在 CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION 函數(shù)下斷點(diǎn)觀察到
- 被 GCD 喚醒或者從第 7 步跳轉(zhuǎn)過(guò)來(lái)的話,處理 GCD,可以在 CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE 函數(shù)下斷點(diǎn)觀察到
- 被 sources 1 喚醒,處理 sources 1,可以在 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION 函數(shù)下斷點(diǎn)觀察到
- 再一次處理 blocks
- 判斷是否退出,不需要退出則跳轉(zhuǎn)回第 2 步
- 通知 observers: kCFRunLoopExit, 退出 run loop
GCD和RunLoop的關(guān)系
當(dāng)調(diào)用 dispatch_async(dispatch_get_main_queue(), block) 時(shí),libDispatch 會(huì)向主線程的RunLoop發(fā)送消息,RunLoop會(huì)被喚醒,并從消息中取得這個(gè) block,并在回調(diào) CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE() 里執(zhí)行這個(gè) block。但這個(gè)邏輯僅限于 dispatch 到主線程,dispatch 到其他線程仍然是由 libDispatch 處理的。那么你肯定會(huì)問(wèn):為什么子線程沒(méi)有這個(gè)和GCD交互的邏輯?原因有二:
主線程runloop是主線程的事件管理者。runloop負(fù)責(zé)何時(shí)讓runloop處理何種事件。所有分發(fā)個(gè)主線程的任務(wù)必須統(tǒng)一交給主線程runloop排隊(duì)處理。舉例:UI操作只能在主線程,不在主線程操作UI會(huì)帶來(lái)很多UI錯(cuò)亂問(wèn)題以及UI更新延遲問(wèn)題。
子線程不接受GCD的交互。因?yàn)樽泳€程不一定會(huì)有runloop。
AutoreleasePool和RunLoop的關(guān)系
App啟動(dòng)后,蘋果在主線程 RunLoop 里注冊(cè)了兩個(gè) Observer,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。
第一個(gè) Observer 監(jiān)視的事件是 Entry(即將進(jìn)入Loop),其回調(diào)內(nèi)會(huì)調(diào)用 _objc_autoreleasePoolPush() 創(chuàng)建自動(dòng)釋放池。其 order 是-2147483647,優(yōu)先級(jí)最高,保證創(chuàng)建釋放池發(fā)生在其他所有回調(diào)之前。
第二個(gè) Observer 監(jiān)視了兩個(gè)事件: BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池;Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來(lái)釋放自動(dòng)釋放池。這個(gè) Observer 的 order 是 2147483647,優(yōu)先級(jí)最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后。
在主線程執(zhí)行的代碼,通常是寫在諸如事件回調(diào)、Timer回調(diào)內(nèi)的。這些回調(diào)會(huì)被 RunLoop 創(chuàng)建好的 AutoreleasePool 環(huán)繞著,所以不會(huì)出現(xiàn)內(nèi)存泄漏,開(kāi)發(fā)者也不必顯示創(chuàng)建 Pool 了。