- 關(guān)于
Run Loop的文章在之前的博客已做過(guò)分析,這里將文章轉(zhuǎn)移到簡(jiǎn)書,并進(jìn)行一些信息補(bǔ)充。
RunLoop 官方文檔 Threading Programming Guide

何時(shí)要用 run loop ?
- 應(yīng)用程序
主線程的 run loop是基礎(chǔ)結(jié)構(gòu)的關(guān)鍵部分。因此,應(yīng)用程序框架提供了運(yùn)行主應(yīng)用程序循環(huán)的代碼,并自動(dòng)啟動(dòng)該循環(huán)。 - 對(duì)于輔助線程,您需要決定是否需要運(yùn)行循環(huán),如果需要,自己配置并啟動(dòng)它。
- 不需要在所有情況下啟動(dòng)線程的運(yùn)行循環(huán)。例如,如果您使用一個(gè)線程來(lái)執(zhí)行一些長(zhǎng)時(shí)間運(yùn)行和預(yù)定的任務(wù),您可能可以避免啟動(dòng)運(yùn)行循環(huán)。
- 運(yùn)行循環(huán)適用于需要與線程進(jìn)行更多交互性的情況。例如,如果你計(jì)劃做以下任何一件事,你需要啟動(dòng)一個(gè)運(yùn)行循環(huán):
1.使用端口或自定義輸入源與其他線程通信。- 例:線程間 NSPort 通訊必須加入 runloop 才可正常通訊
2.在線程上使用計(jì)時(shí)器。
3.在Cocoa應(yīng)用程序中使用任何performSelector方法。
4.保持線程運(yùn)行以執(zhí)行周期性任務(wù)。
一、Runloop 介紹
1、RunLoop 是什么?
運(yùn)行循環(huán)是與線程相關(guān)聯(lián)的基礎(chǔ)設(shè)施的一部分。運(yùn)行循環(huán)是一個(gè)事件處理循環(huán),用于調(diào)度工作和協(xié)調(diào)接收傳入事件。
運(yùn)行循環(huán)的目的是讓線程在有工作要做時(shí)保持忙碌,而在沒(méi)有工作要做時(shí)讓線程休眠。
運(yùn)行循環(huán)管理不是完全自動(dòng)的。您仍然必須設(shè)計(jì)線程的代碼,以在適當(dāng)?shù)臅r(shí)間啟動(dòng)運(yùn)行循環(huán)并響應(yīng)傳入的事件。Cocoa 和 Core Foundation 都提供了run loop對(duì)象來(lái)幫助你配置和管理線程的 run loop。您的應(yīng)用程序不需要顯式地創(chuàng)建這些對(duì)象;
每個(gè)線程,包括應(yīng)用程序的主線程,都有一個(gè)關(guān)聯(lián)的run loop對(duì)象。然而,只有輔助線程需要顯式地運(yùn)行它們的run循環(huán)。作為應(yīng)用程序啟動(dòng)過(guò)程的一部分,應(yīng)用程序框架會(huì)在主線程(main)上自動(dòng)設(shè)置并運(yùn)行run循環(huán)。
簡(jiǎn)單來(lái)說(shuō)即:runloop 是一個(gè)對(duì)象,它提供了一個(gè)入口函數(shù),內(nèi)部是一個(gè) do...while... 循環(huán)(并非通常意義上do...while...),循環(huán)內(nèi) 進(jìn)行事件處理。

2、RunLoop 作用
runloop 的結(jié)構(gòu)和 source:

保持程序不死。--> main 函數(shù)
處理 APP 中的各類事件:觸摸交互、定時(shí)器、performSelect、端口交互... ...
節(jié)省 CPU 資源 --> 有工作時(shí)喚醒,完成/無(wú)任務(wù)時(shí)休眠
3、RunLoop 應(yīng)用
block / timer / 響應(yīng) source0/source1 / GCD 主隊(duì)列 / observe 源
// GCD
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
} // 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
} // 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
}
4、RunLoop 和線程的關(guān)系
4.1)線程和 runloop 一對(duì)一(key - value)關(guān)系
runloop 源碼中可得出 runloop 和 thread 的 dictionary 的key - value關(guān)系;
--> 從簡(jiǎn)介“每個(gè)線程,包括應(yīng)用程序的主線程,都有一個(gè)關(guān)聯(lián)的run loop對(duì)象”也可得知。
// should only be called by Foundation // t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) { if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock); if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); // 創(chuàng)建 runloop: __CFRunLoopCreate()
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); // 存儲(chǔ) runloop: CFDictionarySetValue()
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
// 獲取 runloop: CFDictionaryGetValue()
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock); if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
} // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&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);
}
} return loop;
}
4.2)示例
NSThread *thread = [[NSThread alloc] initWithBlock:^{ // 任務(wù)
NSLog(@"%@---%@",[NSThread currentThread],[[NSThread currentThread] name]); // 定時(shí)器任務(wù)
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"子線程 timer -- hello word");
}];
}];
thread.name = @"my_thread";
[thread start]; </pre>
執(zhí)行結(jié)果:

并沒(méi)有執(zhí)行 timer,為何???
子線程中 runloop 并未開啟,默認(rèn)是關(guān)閉不開啟的
我們?cè)?thread block 中加入代碼:[[NSRunLoop currentRunLoop] run];
再次執(zhí)行:timer 執(zhí)行了

如何停止 runloop 呢?
1. 設(shè)置 timeout;
2.直接結(jié)束對(duì)應(yīng)的線程
通過(guò)某響應(yīng)事件(觸摸/按鈕等)觸發(fā),代碼如下:
NSThread *thread = [[NSThread alloc] initWithBlock:^{ // 任務(wù)
NSLog(@"%@---%@",[NSThread currentThread],[[NSThread currentThread] name]); // 定時(shí)器任務(wù)
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"子線程 timer -- hello word"); // 事件響應(yīng) isStoping=YES --> 退出線程 --> runloop 停止
if (self.isStopping) {
[NSThread exit];
}
}];
[[NSRunLoop currentRunLoop] run];
}];
另:關(guān)于 NSTimer 定時(shí)器,是基于 RunLoop 實(shí)現(xiàn)的(下面會(huì)詳細(xì)分析)
當(dāng)我們啟用 NSTimer 時(shí),并不是按照時(shí)間間隔進(jìn)行循環(huán)調(diào)用的。在定時(shí)器注冊(cè)到 runloop 中后,runloop 會(huì)設(shè)置一個(gè)個(gè)的時(shí)間點(diǎn)進(jìn)行調(diào)用,比如10、20、30...。如果錯(cuò)過(guò)了某個(gè)時(shí)間點(diǎn),定時(shí)器是不會(huì)延時(shí)調(diào)用的,他會(huì)直接等待下一個(gè)時(shí)間點(diǎn)調(diào)用,so 定時(shí)器并不是精準(zhǔn)的。
4.2.1)子線程中使用某些延時(shí)函數(shù)和選擇器時(shí),也必須手動(dòng)開啟 runloop,如下方法:
/**************** Delayed perform ******************/
@interface NSObject (NSDelayedPerforming) - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay inModes:(NSArray<NSRunLoopMode> *)modes; - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay; + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget selector:(SEL)aSelector object:(nullable id)anArgument; + (void)cancelPreviousPerformRequestsWithTarget:(id)aTarget; @end
@interface NSRunLoop (NSOrderedPerform) - (void)performSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg order:(NSUInteger)order modes:(NSArray<NSRunLoopMode> *)modes; - (void)cancelPerformSelector:(SEL)aSelector target:(id)target argument:(nullable id)arg; - (void)cancelPerformSelectorsWithTarget:(id)target; @end
二、RunLoop 的5個(gè)類
A run loop object provides the main interface for adding input sources, timers, and run-loop observers to your run loop and then running it. Every thread has a single run loop object associated with it. In Cocoa, this object is an instance of the NSRunLoop class. In a low-level application, it is a pointer to a CFRunLoopRef opaque type.
-- runloop 對(duì)象提供了一個(gè)主接口(入口),用于向運(yùn)行循環(huán)添加輸入源、計(jì)時(shí)器和運(yùn)行循環(huán)觀察器,然后運(yùn)行它。每個(gè)線程都有一個(gè)與之關(guān)聯(lián)的運(yùn)行循環(huán)對(duì)象。在Cocoa中,這個(gè)對(duì)象是NSRunLoop 類的一個(gè)實(shí)例。在低級(jí)應(yīng)用程序中,它是一個(gè)指向 CFRunLoopRef 不透明類型的指針。
RunLoop 的5個(gè)類:

RunLoop 關(guān)系圖:

1、CFRunLoopRef
<-- typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;
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
pthread_t _pthread;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes; struct _block_item *_blocks_head; struct _block_item *_blocks_tail;
CFTypeRef _counterpart;
};
2、CFRunLoopModeRef <-- __CFRunLoopMode 結(jié)構(gòu)體
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct **__CFRunLoopMode** {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */ CFStringRef _name; // mode name 例如:KCFRunLoopDefaultMode
Boolean _stopped;
char _padding[3];
**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 */ };
RunLoop 的 mode
A run loop mode is a collection of input sources and timers to be monitored and a collection of run loop observers to be notified. Each time you run your run loop, you specify (either explicitly or implicitly) a particular “mode” in which to run. During that pass of the run loop, only sources associated with that mode are monitored and allowed to deliver their events. (Similarly, only observers associated with that mode are notified of the run loop’s progress.) Sources associated with other modes hold on to any new events until subsequent passes through the loop in the appropriate mode.
==》 runloop mode 是要監(jiān)視的輸入源和計(jì)時(shí)器的集合,以及要通知的運(yùn)行循環(huán)觀察者的集合。每次運(yùn)行 runloop 時(shí),都(顯式或隱式)指定要在其中運(yùn)行的特定“mode”。在 runloop 的傳遞過(guò)程中,只監(jiān)視與該模式關(guān)聯(lián)的源,并允許交付它們的事件。(類似地,只有與該模式關(guān)聯(lián)的觀察者才會(huì)被告知運(yùn)行循環(huán)的進(jìn)度。)與其他模式相關(guān)聯(lián)的源 會(huì)保留任何新事件,直到后續(xù)事件以適當(dāng)?shù)哪J酵ㄟ^(guò)循環(huán)。
mode 類型:

runloop mode 關(guān)系圖:

一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer。每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode。如果需要切換 Mode,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。
mode item(CFRunLoopTimerRef / CFRunLoopSourceRef / CFRunLoopObserverRef)
2.1)CFRunLoopTimerRef <-- __CFRunLoopTimer
基于時(shí)間的觸發(fā)器,當(dāng)其加入到 RunLoop 時(shí),RunLoop 會(huì)注冊(cè)對(duì)應(yīng)的時(shí)間點(diǎn),當(dāng)時(shí)間點(diǎn)到時(shí),RunLoop會(huì)被喚醒以執(zhí)行那個(gè)回調(diào)。
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFMutableSetRef _rlModes;
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable */ CFTimeInterval _tolerance; /* mutable */ uint64_t _fireTSR; /* TSR units */ CFIndex _order; /* immutable */ CFRunLoopTimerCallBack _callout; /* immutable */ CFRunLoopTimerContext _context; /* immutable, except invalidation */ };

以定時(shí)器(其底部是一個(gè) CFRunLoopTimerRef)為例,通過(guò)源碼探索 item mode 關(guān)系流程。
創(chuàng)建個(gè) timer 并將其添加到 runloop 的 mode 中:
- (void)timerTest {
NSTimer *timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"timer in home -- %@",[[NSRunLoop currentRunLoop] currentMode]);
}];
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}
流程:
創(chuàng)建定時(shí)器 --> timer 加入到 runloop 中的 mode 中(timer 加到 items 中) --> 即:CFSetAddValue(rl->_commonModeItems, rlt) --> runloop run --> 函數(shù) CFRunLoopRun() 執(zhí)行 --> CFRunLoopRunSpecific() 中 __CFRunLoopRun --> __CFRunLoopDoBlocks(rl, rlm) --> while 循環(huán)所有items --> mode 判斷(doit CFEqual/CFSetContainsValue) --> 執(zhí)行 __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block) --> block() --> function
(item 依賴于 mode mode 依賴于 runloop)
CFRunLoop 歷程部分源碼(runloop完整源碼下載見(jiàn)文章頂部):
1. 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); if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL; if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
} // 將 timer 加入 commonModeItems 里面
**CFSetAddValue(rl->_commonModeItems, rlt);** if (NULL != set) {// 循環(huán),一直加,知道set為null
CFTypeRef context[2] = {rl, rlt}; /* add new item to all common-modes */
// __CFRunLoopAddItemToCommonModes 執(zhí)行 CFRunLoopAddTimer
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
... ...
}
__CFRunLoopUnlock(rl);
}
2. CFRunLoopRunSpecific()
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
3. __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)
__CFRunLoopDoBlocks(rl, rlm);
4. doBlock
static Boolean **__CFRunLoopDoBlocks**(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
if (!rl->_blocks_head) return false; if (!rlm || !rlm->_name) return false;
Boolean did = false; struct _block_item *head = rl->_blocks_head; struct _block_item *tail = rl->_blocks_tail;
rl->_blocks_head = NULL;
rl->_blocks_tail = NULL;
CFSetRef commonModes = rl->_commonModes;
CFStringRef curMode = rlm->_name;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl); struct _block_item *prev = NULL; struct _block_item *item = head; // 循環(huán)所有 item
while (item) { struct _block_item *curr = item;
item = item->_next;
Boolean doit = false; if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) { // CFEqual(curr->_mode, curMode): 當(dāng)前的_mode 和 傳過(guò)來(lái)的curMode 是否相同 // 當(dāng)前_mode是kCFRunLoopCommonModes && 傳來(lái)的curMode是rlmodes里的一員 // doit
doit = **CFEqual**(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && **CFSetContainsValue**(commonModes, curMode));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} if (!doit) prev = curr; if (doit) { if (prev) prev->_next = item; if (curr == head) head = item; if (curr == tail) tail = prev; void (^block)(void) = curr->_block;
CFRelease(curr->_mode);
free(curr); if (doit) { /* block 回調(diào)
**static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void)) {
if (block) {
block();
}
asm __volatile__(""); // thwart tail-call optimization
}** */ **__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block)**;
did = true;
}
Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm); if (head) {
tail->_next = rl->_blocks_head;
rl->_blocks_head = head; if (!rl->_blocks_tail) rl->_blocks_tail = tail;
} return did;
}
tip:通過(guò)上述流程,我們亦可得出定時(shí)器不準(zhǔn)的具體原因:
定時(shí)器所屬 mode 是 kCFRunLoopDefaultMode,當(dāng)頁(yè)面進(jìn)行滑動(dòng)or其他操作時(shí),mode 是 UITrackingRunLoopMode, mode 來(lái)回切換 --> 在“流程4”中:當(dāng)頁(yè)面滑動(dòng)時(shí) mode 不同,doit 為false 后續(xù) block 不執(zhí)行,回調(diào)便不走了 --> 定時(shí)器停了,直到下次 loop 點(diǎn)繼續(xù)。
2.2)CFRunLoopSourceRef <-- __CFRunLoopSource
事件產(chǎn)生的地方,包含 source0 source1。
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order; /* immutable */ CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; /* source0 immutable, except invalidation */ CFRunLoopSourceContext1 version1; /* source1 immutable, except invalidation */ } _context;
};

2.3)CFRunLoopObserverRef <-- __CFRunLoopObserver
觀察者,每個(gè) Observer 都包含了一個(gè)回調(diào)(函數(shù)指針),當(dāng) RunLoop 的狀態(tài)發(fā)生變化時(shí),觀察者就能通過(guò)回調(diào)接受到這個(gè)變化。
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities; /* immutable */ CFIndex _order; /* immutable */ CFRunLoopObserverCallBack _callout; /* immutable */ CFRunLoopObserverContext _context; /* immutable, except invalidation */ };

可觀測(cè)的 時(shí)間點(diǎn) 們:
/* 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), // 剛剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), // 即將退出 loop
kCFRunLoopAllActivities = 0x0FFFFFFFU };
一個(gè) item 可以被同時(shí)加入多個(gè) mode。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會(huì)有效果的。
如果一個(gè) mode 中一個(gè) item 都沒(méi)有,則 RunLoop 會(huì)直接退出,不進(jìn)入循環(huán)。
000)示例 - 簡(jiǎn)單實(shí)現(xiàn) timer 和 事件監(jiān)聽:
主要代碼:
#pragma mark - timer -
- (void)cfTimerDemo {
CFRunLoopTimerContext context = { 0,
((__bridge void *)self),
NULL,
NULL,
NULL
};
CFRunLoopRef rlp = CFRunLoopGetCurrent(); /**
參數(shù)一:用于分配對(duì)象的內(nèi)存
參數(shù)二:在什么是觸發(fā) (距離現(xiàn)在)
參數(shù)三:每隔多少時(shí)間觸發(fā)一次
參數(shù)四:未來(lái)參數(shù)
參數(shù)五:CFRunLoopObserver的優(yōu)先級(jí) 當(dāng)在Runloop同一運(yùn)行階段中有多個(gè)CFRunLoopObserver 正常情況下使用0
參數(shù)六:回調(diào),比如觸發(fā)事件,我就會(huì)來(lái)到這里
參數(shù)七:上下文記錄信息 */ CFRunLoopTimerRef timerRef = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 1, 0, 0, myRunLoopTimerCallBack, &context);
CFRunLoopAddTimer(rlp, timerRef, kCFRunLoopDefaultMode);
} void myRunLoopTimerCallBack(CFRunLoopTimerRef timer, void *info){
NSLog(@"%@---%@",timer,info);
} #pragma mark - observer -
- (void)cfObseverDemo {
CFRunLoopObserverContext context = { 0,
((__bridge void *)self),
NULL,
NULL,
NULL
};
CFRunLoopRef rlp = CFRunLoopGetCurrent(); /**
參數(shù)一:用于分配對(duì)象的內(nèi)存
參數(shù)二:你關(guān)注的事件
kCFRunLoopEntry=(1<<0),
kCFRunLoopBeforeTimers=(1<<1),
kCFRunLoopBeforeSources=(1<<2),
kCFRunLoopBeforeWaiting=(1<<5),
kCFRunLoopAfterWaiting=(1<<6),
kCFRunLoopExit=(1<<7),
kCFRunLoopAllActivities=0x0FFFFFFFU
參數(shù)三:CFRunLoopObserver是否循環(huán)調(diào)用
參數(shù)四:CFRunLoopObserver的優(yōu)先級(jí) 當(dāng)在Runloop同一運(yùn)行階段中有多個(gè)CFRunLoopObserver 正常情況下使用0
參數(shù)五:回調(diào),比如觸發(fā)事件,我就會(huì)來(lái)到這里
參數(shù)六:上下文記錄信息 */ CFRunLoopObserverRef observerRef = CFRunLoopObserverCreate(kCFAllocatorDefault, kCFRunLoopAllActivities, YES, 0, myRunLoopObserverCallBack, &context);
CFRunLoopAddObserver(rlp, observerRef, kCFRunLoopDefaultMode);
} void myRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info){
NSLog(@"%lu-%@",activity,info);
}
三、RunLoop 內(nèi)部邏輯
蘋果文檔: The Run Loop Sequence of Events

RunLoop 核心流程源碼如下,比較長(zhǎng)可以粘下來(lái)再看.
1 /* rl, rlm are locked on entrance and exit */
2 /**
3 * 運(yùn)行run loop
4 *
5 * @param rl 運(yùn)行的RunLoop對(duì)象
6 * @param rlm 運(yùn)行的mode
7 * @param seconds run loop超時(shí)時(shí)間
8 * @param stopAfterHandle true:run loop處理完事件就退出 false:一直運(yùn)行直到超時(shí)或者被手動(dòng)終止
9 * @param previousMode 上一次運(yùn)行的mode
10 *
11 * @return 返回4種狀態(tài)
12 */
13 static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { 14
15 // 獲取系統(tǒng)啟動(dòng)后的CPU運(yùn)行時(shí)間,用于控制超時(shí)時(shí)間
16 uint64_t startTSR = mach_absolute_time(); 17
18 // 判斷當(dāng)前runloop的狀態(tài)是否關(guān)閉
19 if (__CFRunLoopIsStopped(rl)) { 20 __CFRunLoopUnsetStopped(rl);
21 return kCFRunLoopRunStopped; 22 } else if (rlm->_stopped) {
23 return kCFRunLoopRunStopped; 24 rlm->_stopped = false;
25 }
26
27 // mach端口,在內(nèi)核中,消息在端口之間傳遞。 初始為0
28 mach_port_name_t dispatchPort = MACH_PORT_NULL; 29 // 判斷是否為主線程
30 Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ))); 31 // 如果在主線程 && runloop是主線程的runloop && 該mode是commonMode,則給mach端口賦值為主線程收發(fā)消息的端口
32 if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF(); 33
34 #if USE_DISPATCH_SOURCE_FOR_TIMERS
35 mach_port_name_t modeQueuePort = MACH_PORT_NULL; 36 if (rlm->_queue) {
37 // mode賦值為dispatch端口_dispatch_runloop_root_queue_perform_4CF
38 modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
39 if (!modeQueuePort) {
40 CRASH("Unable to get port for run loop mode queue (%d)", -1);
41 }
42 }
43 #endif
44
45 dispatch_source_t timeout_timer = NULL; 46 struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
47 if (seconds <= 0.0) { // instant timeout
48 seconds = 0.0;
49 timeout_context->termTSR = 0ULL; 50 } else if (seconds <= TIMER_INTERVAL_LIMIT) { 51 // seconds為超時(shí)時(shí)間,超時(shí)時(shí)執(zhí)行__CFRunLoopTimeout函數(shù)
52 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT); 53 timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
54 dispatch_retain(timeout_timer);
55 timeout_context->ds = timeout_timer; 56 timeout_context->rl = (CFRunLoopRef)CFRetain(rl); 57 timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds); 58 dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
59 dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
60 dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
61 uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL); 62 dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
63 dispatch_resume(timeout_timer);
64 } else { // infinite timeout 65 // 永不超時(shí)
66 seconds = 9999999999.0;
67 timeout_context->termTSR = UINT64_MAX; 68 }
69
70 // 標(biāo)志位默認(rèn)為true
71 Boolean didDispatchPortLastTime = true;
72 //記錄最后runloop狀態(tài),用于return
73 int32_t retVal = 0;
74 do { 75 // 初始化一個(gè)存放內(nèi)核消息的緩沖池
76 uint8_t msg_buffer[3 * 1024];
77 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
78 mach_msg_header_t *msg = NULL; 79 mach_port_t livePort = MACH_PORT_NULL; 80 #elif DEPLOYMENT_TARGET_WINDOWS
81 HANDLE livePort = NULL; 82 Boolean windowsMessageReceived = false;
83 #endif
84 // 取所有需要監(jiān)聽的port
85 __CFPortSet waitSet = rlm->_portSet;
86
87 // 設(shè)置RunLoop為可以被喚醒狀態(tài)
88 __CFRunLoopUnsetIgnoreWakeUps(rl);
89
90 /// 2\. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)。
91 if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); 92 if (rlm->_observerMask & kCFRunLoopBeforeSources) 93 /// 3\. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)。
94 __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
95
96 /// 執(zhí)行被加入的block
97 __CFRunLoopDoBlocks(rl, rlm);
98 /// 4\. RunLoop 觸發(fā) Source0 (非port) 回調(diào)。
99 Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); 100 if (sourceHandledThisLoop) { 101 /// 執(zhí)行被加入的block
102 __CFRunLoopDoBlocks(rl, rlm); 103 } 104
105 // 如果沒(méi)有 Sources0 事件處理 并且 沒(méi)有超時(shí),poll為false 106 // 如果有 Sources0 事件處理 或者 超時(shí),poll都為true
107 Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); 108 // 第一次do..whil循環(huán)不會(huì)走該分支,因?yàn)閐idDispatchPortLastTime初始化是true
109 if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { 110 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
111 // 從緩沖區(qū)讀取消息
112 msg = (mach_msg_header_t *)msg_buffer; 113 /// 5\. 如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息。
114 if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) { 115 // 如果接收到了消息的話,前往第9步開始處理msg
116 goto handle_msg; 117 } 118 #elif DEPLOYMENT_TARGET_WINDOWS
119 if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) { 120 goto handle_msg; 121 } 122 #endif
123 } 124
125 didDispatchPortLastTime = false; 126 /// 6.通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)。
127 if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); 128 // 設(shè)置RunLoop為休眠狀態(tài)
129 __CFRunLoopSetSleeping(rl); 130 // do not do any user callouts after this point (after notifying of sleeping) 131
132 // Must push the local-to-this-activation ports in on every loop 133 // iteration, as this mode could be run re-entrantly and we don't 134 // want these ports to get serviced.
135
136 __CFPortSetInsert(dispatchPort, waitSet); 137
138 __CFRunLoopModeUnlock(rlm); 139 __CFRunLoopUnlock(rl); 140
141 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
142 #if USE_DISPATCH_SOURCE_FOR_TIMERS
143
144 // 這里有個(gè)內(nèi)循環(huán),用于接收等待端口的消息 145 // 進(jìn)入此循環(huán)后,線程進(jìn)入休眠,直到收到新消息才跳出該循環(huán),繼續(xù)執(zhí)行run loop
146 do { 147 if (kCFUseCollectableAllocator) { 148 objc_clear_stack(0); 149 memset(msg_buffer, 0, sizeof(msg_buffer)); 150 } 151
152 msg = (mach_msg_header_t *)msg_buffer; 153 /// 7.接收 waitSet 端口的消息
154 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY); 155 // 收到消息之后,livePort的值為msg->msgh_local_port,
156 if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { 157 // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
158 while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue)); 159 if (rlm->_timerFired) { 160 // Leave livePort as the queue port, and service timers below
161 rlm->_timerFired = false; 162 break; 163 } else { 164 if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); 165 } 166 } else { 167 // Go ahead and leave the inner loop.
168 break; 169 } 170 } while (1); 171 #else
172 if (kCFUseCollectableAllocator) { 173 objc_clear_stack(0); 174 memset(msg_buffer, 0, sizeof(msg_buffer)); 175 } 176 msg = (mach_msg_header_t *)msg_buffer; 177 /// 7\. 調(diào)用 mach_msg 等待接受 mach_port 的消息。線程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒。 178 /// ? 一個(gè)基于 port 的Source 的事件。 179 /// ? 一個(gè) Timer 到時(shí)間了 180 /// ? RunLoop 自身的超時(shí)時(shí)間到了 181 /// ? 被其他什么調(diào)用者手動(dòng)喚醒
182 __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY); 183 #endif
184
185
186 #elif DEPLOYMENT_TARGET_WINDOWS
187 // Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
188 __CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived); 189 #endif
190
191 __CFRunLoopLock(rl); 192 __CFRunLoopModeLock(rlm); 193
194 // Must remove the local-to-this-activation ports in on every loop 195 // iteration, as this mode could be run re-entrantly and we don't 196 // want these ports to get serviced. Also, we don't want them left 197 // in there if this function returns.
198
199 __CFPortSetRemove(dispatchPort, waitSet); 200
201 __CFRunLoopSetIgnoreWakeUps(rl); 202
203 // user callouts now OK again 204 //取消runloop的休眠狀態(tài)
205 __CFRunLoopUnsetSleeping(rl); 206 /// 8\. 通知 Observers: RunLoop 的線程剛剛被喚醒了。
207 if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); 208
209 /// 收到消息,處理消息。
210 handle_msg:; 211 __CFRunLoopSetIgnoreWakeUps(rl); 212
213 #if DEPLOYMENT_TARGET_WINDOWS
214 if (windowsMessageReceived) { 215 // These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
216 __CFRunLoopModeUnlock(rlm); 217 __CFRunLoopUnlock(rl); 218
219 if (rlm->_msgPump) { 220 rlm->_msgPump(); 221 } else { 222 MSG msg; 223 if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) { 224 TranslateMessage(&msg); 225 DispatchMessage(&msg); 226 } 227 } 228
229 __CFRunLoopLock(rl); 230 __CFRunLoopModeLock(rlm); 231 sourceHandledThisLoop = true; 232
233 // To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced 234 // Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later. 235 // NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
236 __CFRunLoopSetSleeping(rl); 237 __CFRunLoopModeUnlock(rlm); 238 __CFRunLoopUnlock(rl); 239
240 __CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL); 241
242 __CFRunLoopLock(rl); 243 __CFRunLoopModeLock(rlm); 244 __CFRunLoopUnsetSleeping(rl); 245 // If we have a new live port then it will be handled below as normal
246 } 247
248
249 #endif
250 if (MACH_PORT_NULL == livePort) { 251 CFRUNLOOP_WAKEUP_FOR_NOTHING(); 252 // handle nothing
253 } else if (livePort == rl->_wakeUpPort) { 254 CFRUNLOOP_WAKEUP_FOR_WAKEUP(); 255 // do nothing on Mac OS
256 #if DEPLOYMENT_TARGET_WINDOWS
257 // Always reset the wake up port, or risk spinning forever
258 ResetEvent(rl->_wakeUpPort); 259 #endif
260 } 261 #if USE_DISPATCH_SOURCE_FOR_TIMERS
262 else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { 263 CFRUNLOOP_WAKEUP_FOR_TIMER(); 264 /// 9\. 處理 喚醒時(shí)收到的消息 265 /// 9.1 如果一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)Timer的回調(diào)。
266 if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { 267 // Re-arm the next timer, because we apparently fired early
268 __CFArmNextTimerInMode(rlm, rl); 269 } 270 } 271 #endif
272 #if USE_MK_TIMER_TOO
273 else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) { 274 CFRUNLOOP_WAKEUP_FOR_TIMER(); 275 // On Windows, we have observed an issue where the timer port is set before the time which we requested it to be set. For example, we set the fire time to be TSR 167646765860, but it is actually observed firing at TSR 167646764145, which is 1715 ticks early. The result is that, when __CFRunLoopDoTimers checks to see if any of the run loop timers should be firing, it appears to be 'too early' for the next timer, and no timers are handled. 276 // In this case, the timer port has been automatically reset (since it was returned from MsgWaitForMultipleObjectsEx), and if we do not re-arm it, then no timers will ever be serviced again unless something adjusts the timer list (e.g. adding or removing timers). The fix for the issue is to reset the timer here if CFRunLoopDoTimers did not handle a timer itself. 9308754
277 if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) { 278 // Re-arm the next timer
279 __CFArmNextTimerInMode(rlm, rl); 280 } 281 } 282 #endif
283 /// 9.2 如果有dispatch到main_queue的block,執(zhí)行block
284 else if (livePort == dispatchPort) { 285 CFRUNLOOP_WAKEUP_FOR_DISPATCH(); 286 __CFRunLoopModeUnlock(rlm); 287 __CFRunLoopUnlock(rl); 288 _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL); 289 #if DEPLOYMENT_TARGET_WINDOWS
290 void *msg = 0; 291 #endif
292 __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg); 293 _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL); 294 __CFRunLoopLock(rl); 295 __CFRunLoopModeLock(rlm); 296 sourceHandledThisLoop = true; 297 didDispatchPortLastTime = true; 298 } else { 299 /// 9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了,處理這個(gè)事件
300 CFRUNLOOP_WAKEUP_FOR_SOURCE(); 301 // Despite the name, this works for windows handles as well
302 CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort); 303 if (rls) { 304 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
305 mach_msg_header_t *reply = NULL; 306 sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop; 307 if (NULL != reply) { 308 (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL); 309 CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply); 310 } 311 #elif DEPLOYMENT_TARGET_WINDOWS
312 sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop; 313 #endif
314 } 315 } 316 #if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
317 if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg); 318 #endif
319
320 /// 執(zhí)行加入到Loop的block
321 __CFRunLoopDoBlocks(rl, rlm); 322
323 if (sourceHandledThisLoop && stopAfterHandle) { 324 /// 進(jìn)入loop時(shí)參數(shù)說(shuō)處理完事件就返回。
325 retVal = kCFRunLoopRunHandledSource; 326 } else if (timeout_context->termTSR < mach_absolute_time()) { 327 /// 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間了
328 retVal = kCFRunLoopRunTimedOut; 329 } else if (__CFRunLoopIsStopped(rl)) { 330 /// 被外部調(diào)用者強(qiáng)制停止了
331 __CFRunLoopUnsetStopped(rl); 332 retVal = kCFRunLoopRunStopped; 333 } else if (rlm->_stopped) { 334 /// 自動(dòng)停止了
335 rlm->_stopped = false; 336 retVal = kCFRunLoopRunStopped; 337 } else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { 338 /// source/timer/observer一個(gè)都沒(méi)有了
339 retVal = kCFRunLoopRunFinished; 340 } 341 /// 如果沒(méi)超時(shí),mode里沒(méi)空,loop也沒(méi)被停止,那繼續(xù)loop。
342 } while (0 == retVal); 343
344 if (timeout_timer) { 345 dispatch_source_cancel(timeout_timer); 346 dispatch_release(timeout_timer); 347 } else { 348 free(timeout_context); 349 } 350
351 return retVal; 352 }
通過(guò)這里也可驗(yàn)證上述“runloop 是一個(gè)對(duì)象,它提供了一個(gè)入口函數(shù),其內(nèi)部是一個(gè) do...while... 循環(huán),循環(huán)內(nèi) 進(jìn)行事件處理”。