什么是RunLoop
一般來(lái)講,一個(gè)線(xiàn)程一次只能執(zhí)行一個(gè)任務(wù),執(zhí)行完任務(wù)后線(xiàn)程就會(huì)退出,避免資源占用。但是某些情況下,我們需要線(xiàn)程執(zhí)行完畢后不退出,而是處于等待接受事件并處理的常駐狀態(tài),例如程序運(yùn)行期間一直存在的主線(xiàn)程。
這種模式通常稱(chēng)作 Event Loop,在很多系統(tǒng)和框架里都有實(shí)現(xiàn),比如 Node.js 的事件處理,比如 Windows 程序的消息循環(huán),再比如 OSX/iOS 里的 RunLoop。
結(jié)合蘋(píng)果在文檔里的說(shuō)明,RunLoop 的概念如下:
run loop是一個(gè)event processing loop (事件處理循環(huán)),用于管理需要處理的事件和消息。run loop的目的在于讓線(xiàn)程有任務(wù)時(shí)處理,無(wú)任務(wù)時(shí)休眠,節(jié)省CPU資源,優(yōu)化性能。
OS X/IOS系統(tǒng)中提供了兩個(gè)對(duì)象實(shí)現(xiàn)RunLoop:NSRunLoop和CFRunLoopRef
CFRunLoopRef: 存在CoreFoundation框架內(nèi),它提供了純C函數(shù)的API,所有這些線(xiàn)程API都是線(xiàn)程安全的,可以從任何線(xiàn)程調(diào)用。
NSRunLoop: 存在于Foundation,是基于CFRunLoopRef的封裝,提供了面向?qū)ο驛PI,但是這些API不是線(xiàn)程安全的,配置其他線(xiàn)程的runLoop都有可能發(fā)生意料之外的結(jié)果甚至crash。
CFRunLoopRef 的代碼是開(kāi)源的,你可以在這里 http://opensource.apple.com/tarballs/CF/ 下載到整個(gè) CoreFoundation 的源碼來(lái)查看。
Swift 開(kāi)源后,蘋(píng)果又維護(hù)了一個(gè)跨平臺(tái)的 CoreFoundation 版本:https://github.com/apple/swift-corelibs-foundation/,這個(gè)版本的源碼可能和現(xiàn)有 iOS 系統(tǒng)中的實(shí)現(xiàn)略不一樣,但更容易編譯,而且已經(jīng)適配了 Linux/Windows。)
RunLoop 與線(xiàn)程的關(guān)系
獲取線(xiàn)程的runLoop:
- CFRunLoop:CFRunLoopGetCurrent()、CFRunLoopGetMain();
- NSRunLoop:[NSRunLoop currentRunLoop]、[NSRunLoop mainRunLoop];
注意:同一個(gè)thread線(xiàn)程對(duì)應(yīng)的 NSRunLoop 和 CFRunLoop 引用了同一runLoop。NSRunLoop提供了
getCFRunLoop方法獲取對(duì)應(yīng)的CFRunLoop
它們的底層實(shí)現(xiàn)如下:
獲取主線(xiàn)程對(duì)應(yīng)的runLoop
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
獲取線(xiàn)程對(duì)應(yīng)的runLoop
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
可以看出,兩個(gè)函數(shù)最終都是調(diào)用 _CFRunLoopGet0(pthread_t t),pthread_main_thread_np()獲取主線(xiàn)程(實(shí)際對(duì)外Api為pthread_main_np()),pthread_self()獲取當(dāng)前線(xiàn)程,內(nèi)部邏輯如下:
全局的Dictionary,key: pthread_t, value: CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
自動(dòng)鎖,用于線(xiàn)程安全
static CFSpinLock_t loopsLock = CFSpinLockInit;
獲取線(xiàn)程 pthread_t 對(duì)應(yīng)的 runLoop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (!__CFRunLoops) {
// 第一次訪(fǎng)問(wèn)CFRunLoops時(shí),初始化全局MutableDictionary,并為主線(xiàn)程創(chuàng)建一個(gè) runLoop
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
}
// 直接從 Dictionary 里獲取
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
// 獲取字典_CFRunLoops中 key 為 thread 的 runLoop
if (!loop) {
// 獲取不到,為 thread 創(chuàng)建一個(gè)runLoop,并保存到_CFRunLoops
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
}
if (pthread_equal(t, pthread_self())) {
/// 注冊(cè)一個(gè)回調(diào),當(dāng)線(xiàn)程銷(xiāo)毀時(shí),順便也銷(xiāo)毀其對(duì)應(yīng)的 RunLoop
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
以上源碼可以看出:
-
RunLoop和線(xiàn)程之間是一一對(duì)應(yīng)的,其關(guān)系保存在一個(gè)全局的Dictionary,其中key為p_thread,value為CFRunLoopRef。 - 懶加載創(chuàng)建runLoop。線(xiàn)程剛創(chuàng)建時(shí)并沒(méi)有RunLoop,如果你不主動(dòng)獲取,那它一直不會(huì)創(chuàng)建。RunLoop的創(chuàng)建是發(fā)生在第一次獲取的。銷(xiāo)毀發(fā)生在線(xiàn)程結(jié)束時(shí)執(zhí)行的回調(diào)函數(shù)__CFFinalizeRunLoop。
- 蘋(píng)果提供的獲取線(xiàn)程的runloop的API只有兩個(gè),所以,一個(gè)線(xiàn)程只能在自己內(nèi)部獲取其對(duì)應(yīng)runLoop(主線(xiàn)程除外,每個(gè)線(xiàn)程都能獲取到主線(xiàn)程)
以下為 p_thread 銷(xiāo)毀時(shí)的回調(diào)函數(shù)__CFFinalizeRunLoop,目的是清除runLoop上所有的輸入源source,可跳過(guò)不看:
thread線(xiàn)程銷(xiāo)毀時(shí)的回調(diào)函數(shù)
CF_PRIVATE void __CFFinalizeRunLoop(uintptr_t data) {
CFRunLoopRef rl = NULL;
if (data <= 1) {
// 從全局字典 __CFRunLoops 中移除相應(yīng)thread
if (__CFRunLoops) {
rl = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(pthread_self()));
if (rl) CFRetain(rl);
CFDictionaryRemoveValue(__CFRunLoops, pthreadPointer(pthread_self()));
}
} else {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(data - 1), (void (*)(void *))__CFFinalizeRunLoop);
}
if (rl && CFRunLoopGetMain() != rl) {
// 移除runLoop中Set<Mode>集合里所有mode的source
CFArrayRef array = CFRunLoopCopyAllModes(rl);
for (CFIndex idx = CFArrayGetCount(array); idx--;) {
CFStringRef modeName = (CFStringRef)CFArrayGetValueAtIndex(array, idx);
__CFRunLoopRemoveAllSources(rl, modeName);
}
// 移除mode中Set<commonModes>所有的 source
__CFRunLoopRemoveAllSources(rl, kCFRunLoopCommonModes);
CFRelease(array);
}
if (rl) CFRelease(rl);
}
RunLoop相關(guān)類(lèi)
在CoreFoundation中關(guān)于RunLoop有5個(gè)類(lèi):
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopTimerRef
- CFRunLoopObserverRef
它們的關(guān)系如下:

一個(gè)RunLoop包含若干個(gè)Mode,每個(gè)Mode又包含若干個(gè)Source/Timer/Observer。
CFRunLoopSourceRef是事件產(chǎn)生的地方。它有兩個(gè)版本:Source0和Source1
-
Source0:處理App內(nèi)部事件,App自己負(fù)責(zé)管理,不能主動(dòng)觸發(fā)事件,需要調(diào)用CFRunLoopSourceSignal(source)將source標(biāo)記為待處理,然后手動(dòng)調(diào)用CFRunLoopWakeUp(runloop)來(lái)喚醒 RunLoop,讓其處理這個(gè)事件如UIEvent、CFSocket -
Source1:由RunLoop和內(nèi)核管理,mach_port驅(qū)動(dòng),用于通過(guò)內(nèi)核和其他線(xiàn)程發(fā)送消息,能主動(dòng)喚醒RunLoop的線(xiàn)程,如CFMachPort、CFMessagePort。
具體內(nèi)部結(jié)構(gòu)如下,可跳過(guò)不看:
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits; source0只有在被標(biāo)記為Signaled狀態(tài),才會(huì)被處理
pthread_mutex_t _lock;
CFIndex _order; /* immutable */
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; source0
CFRunLoopSourceContext1 version1; source1
} _context;
};
source0:
typedef struct {
CFIndex version; // 區(qū)分source0和source1,0:source0, 1:source1
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
void (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode); 回調(diào)函數(shù),注冊(cè)
void (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode); 移除source的回調(diào)函數(shù),
void (*perform)(void *info); source0事件發(fā)生的回調(diào)函數(shù)
} CFRunLoopSourceContext;
source1:
typedef struct {
CFIndex version; 區(qū)分source0和source1,0:source0, 1:source1
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
mach_port_t (*getPort)(void *info); 函數(shù)指針,返回source1對(duì)應(yīng)的port端口
void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info); 回調(diào)函數(shù),source1事件發(fā)生時(shí)觸發(fā)
} CFRunLoopSourceContext1;
-
Port-Base Sources(基于端口souce1):Cocoa和Core Foundation提供內(nèi)置支持。在Cocoa中只需創(chuàng)建一個(gè)port對(duì)象,并使用NSPort的方法將該端口添加到runLoop中,port對(duì)象自動(dòng)為您處理所需source的創(chuàng)建和配置。在CoreFoundation中,必須手動(dòng)創(chuàng)建port及其runLoop source
CFRunLoopObserverRef是runLoop的觀(guān)察者,內(nèi)部包含觀(guān)察的runLoop,以及runLoop狀態(tài)發(fā)生變化時(shí)的回調(diào)指針,只有CoreFoundation框架提供了創(chuàng)建observer的API。
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities; /* immutable */
CFIndex _order; /* immutable */
CFRunLoopObserverCallBack _callout; runLoop發(fā)生變化時(shí)執(zhí)行的回調(diào)函數(shù)
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};
// CFRunLoopObserverCallBack是函數(shù)指針
typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);
runLoopObserver觀(guān)察的時(shí)間點(diǎn)有以下幾個(gè):
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), 即將進(jìn)入runLoop
kCFRunLoopBeforeTimers = (1UL << 1), 即將處理Timer
kCFRunLoopBeforeSources = (1UL << 2), 即將處理 Source
kCFRunLoopBeforeWaiting = (1UL << 5), 即將進(jìn)入休眠
kCFRunLoopAfterWaiting = (1UL << 6), 從休眠中喚醒
kCFRunLoopExit = (1UL << 7), 即將退出 runLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
CFRunLoopTimerRef是基于時(shí)間的觸發(fā)器,包含了時(shí)間長(zhǎng)度和回調(diào)函數(shù),當(dāng) timer 加入 RunLoop 后,RunLoop 會(huì)注冊(cè)對(duì)應(yīng)的時(shí)間點(diǎn),在時(shí)間點(diǎn)上,RunLoop 會(huì)被喚醒執(zhí)行 timer 中的函數(shù)回調(diào),結(jié)構(gòu)如下:
CFRuntimeBase _base;
uint16_t _bits; 標(biāo)記timer執(zhí)行的狀態(tài)
pthread_mutex_t _lock;
CFRunLoopRef _runLoop; timer所在的runLoop
CFMutableSetRef _rlModes; timer所在的runLoopMode
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable */
CFTimeInterval _tolerance; /* mutable */
uint64_t _fireTSR; 觸發(fā)時(shí)間 fire Time
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; 回調(diào)函數(shù)
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
Timer 有個(gè)屬性叫做 Tolerance (寬容度),標(biāo)示了當(dāng)時(shí)間點(diǎn)到后,容許有多少最大誤差。
CFRunLoopMode
mode是source/timer/observer的容器,三者被統(tǒng)稱(chēng)為 mode item,結(jié)構(gòu)大致如下:
struct __CFRunLoopMode {
CFStringRef _name; 唯一標(biāo)識(shí)符
Boolean _stopped;
CFMutableSetRef _sources0; Set(source0)
CFMutableSetRef _sources1; Set(source0)
CFMutableArrayRef _observers; Array(source0)
CFMutableArrayRef _timers; Array(source0)
...
}
自定義Model只需要指定任意的名稱(chēng)即可,但是必須確保添加一個(gè)或多個(gè)Source/Timer/Observer到Mode中。
但是蘋(píng)果并沒(méi)有暴露創(chuàng)建runLoopMode的API,只給出了如下接口:
管理Mode的接口
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName)
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
管理mode item 的接口 (source/timer/observer)
以下接口功能大同小異,分別為:是否包含 mode item、添加item、移除item
Boolean CFRunLoopContainsSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
void CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
Boolean CFRunLoopContainsObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
void CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
Boolean CFRunLoopContainsTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef modeName);
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef modeName);
void CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef modeName);
以上函數(shù),我們只能通過(guò)參數(shù) mode name操作mode。
然而事實(shí)上當(dāng)你傳入一個(gè)新的 mode name 時(shí),如果 RunLoop內(nèi)部沒(méi)有對(duì)應(yīng)mode時(shí),RunLoop會(huì)自動(dòng)幫你創(chuàng)建對(duì)應(yīng)的CFRunLoopModeRef,其底層調(diào)用函數(shù)是__CFRunLoopFindMode(runloop, modeName, create),內(nèi)部邏輯如下:
1.runLoop中查找指定name的 Mode
2.沒(méi)找到,是否自動(dòng)創(chuàng)建名為mode name 的Mode,若否返回NULL,否則繼續(xù)向下執(zhí)行
3.創(chuàng)建一個(gè)的Mode,_name為傳入的參數(shù)名mode name
4.初始化Mode中各個(gè)成員變量
5.將Mode添加到runLoop的_modes集合中
代碼大致實(shí)現(xiàn)如下,可跳過(guò)不看:
/* call with rl locked, returns mode locked */
/**
在runloop中查找指定 mode name的 Mode
@param rl 待搜索的runLoop
@param modeName 指定mode的名稱(chēng)
@param create 如果runLoop中不存在指定mode,是否創(chuàng)建一個(gè)
*/
static CFRunLoopModeRef __CFRunLoopFindMode(CFRunLoopRef rl, CFStringRef modeName, Boolean create) {
// 1.runLoop中查找指定 Mode
CFRunLoopModeRef rlm;
rlm = (CFRunLoopModeRef)CFSetGetValue(rl->_modes, &srlm);
if (NULL != rlm) { return rlm; }
// 2.沒(méi)找到,是否自動(dòng)創(chuàng)建名稱(chēng)為mode name 的Mode
if (!create) { return NULL;
// 3.創(chuàng)建一個(gè)指定名稱(chēng)的Mode
rlm = (CFRunLoopModeRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopModeTypeID, sizeof(struct __CFRunLoopMode) - sizeof(CFRuntimeBase), NULL);
if (NULL == rlm) {
return NULL;
}
// 4.初始化runLoopMode內(nèi)部成員變量值
__CFRunLoopLockInit(&rlm->_lock);
rlm->_name = CFStringCreateCopy(kCFAllocatorSystemDefault, modeName);
rlm->_stopped = false;
rlm->_portToV1SourceMap = NULL;
rlm->_sources0 = NULL;
rlm->_sources1 = NULL;
rlm->_observers = NULL;
rlm->_timers = NULL;
rlm->_observerMask = 0;
rlm->_portSet = __CFPortSetAllocate();
rlm->_timerSoftDeadline = UINT64_MAX;
rlm->_timerHardDeadline = UINT64_MAX;
// 通過(guò)GCD創(chuàng)建timer定時(shí)器
#if USE_DISPATCH_SOURCE_FOR_TIMERS
rlm->_timerFired = false;
rlm->_queue = _dispatch_runloop_root_queue_create_4CF("Run Loop Mode Queue", 0);
mach_port_t queuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
if (queuePort == MACH_PORT_NULL) CRASH("*** Unable to create run loop mode queue port. (%d) ***", -1);
rlm->_timerSource = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, rlm->_queue);
__block Boolean *timerFiredPointer = &(rlm->_timerFired);
dispatch_source_set_event_handler(rlm->_timerSource, ^{
*timerFiredPointer = true;
});
_dispatch_source_set_runloop_timer_4CF(rlm->_timerSource, DISPATCH_TIME_FOREVER, DISPATCH_TIME_FOREVER, 321);
dispatch_resume(rlm->_timerSource);
#endif
// 通過(guò)XNU內(nèi)核創(chuàng)建timer定時(shí)器
#if USE_MK_TIMER_TOO
rlm->_timerPort = mk_timer_create();
#endif
ret = __CFPortSetInsert(rl->_wakeUpPort, rlm->_portSet);
CFSetAddValue(rl->_modes, rlm);
return rlm;
}
CFRunLoopRef
CFRunLoopRef是執(zhí)行Loop的類(lèi)型,包含若干mode,每次運(yùn)行Loop只能指定其中一個(gè)Mode(保存為_(kāi)currentMode),僅監(jiān)聽(tīng)和傳遞與該Model相關(guān)的Source/Timer/Observer,防止互相干擾。如果需要切換 Mode,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入。大致結(jié)構(gòu)如下:
struct __CFRunLoop {
__CFPort _wakeUpPort; 用于喚醒線(xiàn)程的port,調(diào)用CFRunLoopWakeUp(runLoop)
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread; runLoop對(duì)應(yīng)的線(xiàn)程
CFMutableSetRef _commonModes; 通用Set(Model)
CFMutableSetRef _commonModeItems; 通用Set(Source/Observer/Timer)
CFRunLoopModeRef _currentMode; 當(dāng)前的Mode
CFMutableSetRef _modes; Set(Model)
};
_commonModes保存common標(biāo)記的Modes;
_commonModeItems保存common標(biāo)記的source/timer/observer;
每當(dāng)RunLoop發(fā)生變化時(shí),就會(huì)將_commonModeItems里的內(nèi)容添加到_commonModes內(nèi)部每個(gè)Mode里。
當(dāng)Mode以Common形式標(biāo)記自己并添加到runLoop上時(shí),實(shí)際是將runLoopMode的_name添加到RunLoop的_commonModes。
RunLoop內(nèi)部邏輯
根據(jù)蘋(píng)果文檔說(shuō)明,RunLoop內(nèi)部邏輯大致如下:
1. Notify observers that the run loop has been entered.
2. Notify observers that any ready timers are about to fire.
3. Notify observers that any input sources that are not port based are about to fire
4. Fire any non-port-based input sources that are ready to fire.
5. If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9.
6. Notify observers that the thread is about to sleep.
7. Put the thread to sleep until one of the following events occurs:
- An event arrives for a port-based input source.
- A timer fires.
- The timeout value set for the run loop expires.
- The run loop is explicitly woken up.
8. Notify observers that the thread just woke up.
9. Process the pending event.
- If a user-defined timer fired, process the timer event and restart the loop. Go to step 2.
- If an input source fired, deliver the event.
- If the run loop was explicitly woken up but has not yet timed out, restart the loop. Go to step 2.
10.Notify observers that the run loop has exited.
這里附上網(wǎng)圖,更清晰:

由于對(duì)Timer和Source的監(jiān)聽(tīng)通知的傳遞是在事件發(fā)生之前,因此通知時(shí)間和實(shí)際事件的時(shí)間可能存在差異
內(nèi)部代碼大致如下:
//使用defaultMode啟動(dòng)runLoop
void CFRunLoopRun(void) {
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
//指定mode啟動(dòng)runLoop,并設(shè)置runLoop超時(shí)時(shí)間
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
// runLoop實(shí)現(xiàn)
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 根據(jù) modeName 找到對(duì)應(yīng)的mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
// mode不存在,或mode里沒(méi)有source/timer,直接返回
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
// 替換runLoop當(dāng)前的mode,runLoop一次只能執(zhí)行一個(gè)mode
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
// 1.通知observer,runLoop 即將進(jìn)入Loop循環(huán)
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 進(jìn)入Loop循環(huán)
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 10.通知observer,runLoop 即將退出
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
// 指定model執(zhí)行完畢后_currentMode替換為上一次mode
rl->_currentMode = previousMode;
return result;
}
// runLoop循環(huán)體
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
do {
// 2.通知runLoop,即將觸發(fā)Timer回調(diào)
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 3.通知runLoop,即將觸發(fā)source0回調(diào)
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 執(zhí)行被加入的block
__CFRunLoopDoBlocks(rl, rlm);
// 4.觸發(fā)source0回調(diào)
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
// 執(zhí)行被加入的block
__CFRunLoopDoBlocks(rl, rlm);
// 5.如果有 Source1 (基于port) 處于 ready 狀態(tài),直接處理這個(gè) Source1 然后跳轉(zhuǎn)去 步驟9 處理消息
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)
if (hasMsg) goto handle_msg;
}
// 6.通知observer,runloop的線(xiàn)程即將進(jìn)入休眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting))
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
// 線(xiàn)程進(jìn)入休眠
__CFRunLoopSetSleeping(rl);
/// 7. 調(diào)用 mach_msg 等待接受 mach_port 的消息。線(xiàn)程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒:
/// ? 一個(gè)基于 port 的Source 的事件。
/// ? 一個(gè) Timer 到時(shí)間了
/// ? RunLoop 自身的超時(shí)時(shí)間到了
/// ? 被其他什么調(diào)用者手動(dòng)喚醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY) {
mach_msg(msg, MACH_RCV_MSG, 0, msg->msgh_size, port, timeout, MACH_PORT_NULL);
}
// 喚醒runLoop
__CFRunLoopSetIgnoreWakeUps(rl);
__CFRunLoopUnsetSleeping(rl);
// 8.通知observer,runLoop的線(xiàn)程剛剛被喚醒
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
// 收到消息,處理消息
// 9.1 如果是一個(gè) Timer 到時(shí)間了,觸發(fā)這個(gè)timer的回調(diào)(timer可能來(lái)自GCD的dispatch_source_或者XNU的mk_timer創(chuàng)建的計(jì)時(shí)器)
if (msg_is_timer) {
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
}
// 9.2 如果有dispatch到main_queue的block,執(zhí)行block
else if (msg_is_dispatch_main_queue) {
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}
// 9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了,處理這個(gè)事件
else {
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
if (sourceHandledThisLoop) {
mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
}
// 執(zhí)行加入到Loop的block
__CFRunLoopDoBlocks(rl, rlm);
if (sourceHandledThisLoop && stopAfterHandle) {
// 進(jìn)入loop時(shí)參數(shù)說(shuō)處理完事件就返回。
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
// 超出傳入?yún)?shù)標(biāo)記的超時(shí)時(shí)間
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
// 被外部調(diào)用者強(qiáng)制停止了,即調(diào)用CFRunLoopStop(runloop)
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
// 外部調(diào)用強(qiáng)制停止
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
// mode中 source/timer/observer為空
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}
以下幾個(gè)函數(shù)貫穿runLoop回調(diào)過(guò)程:
void __CFRunLoopDoObservers()
Boolean __CFRunLoopDoBlocks()
Boolean __CFRunLoopDoTimers()
Boolean __CFRunLoopDoSources0()
Boolean __CFRunLoopDoSource1()
這些函數(shù)內(nèi)部最終調(diào)用如下函數(shù):
void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void))
void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info)
void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info)
void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__( void *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info)
void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg)
因此runLoop的執(zhí)行過(guò)程也可大致表示如下:
int32_t __CFRunLoopRun() {
/// 1. 通知Observers,即將進(jìn)入RunLoop
/// 此處有Observer會(huì)創(chuàng)建AutoreleasePool: _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
do {
/// 2. 通知 Observers: 即將觸發(fā) Timer 回調(diào)。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: 即將觸發(fā) Source (非基于port的,Source0) 回調(diào)。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 4. 觸發(fā) Source0 (非基于port的) 回調(diào)。
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 6. 通知Observers,即將進(jìn)入休眠
/// 此處有Observer釋放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);
/// 7. sleep to wait msg.
mach_msg() -> mach_msg_trap();
/// 8. 通知Observers,線(xiàn)程被喚醒
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);
/// 9. 如果是被Timer喚醒的,回調(diào)Timer
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
/// 9. 如果是被dispatch喚醒的,執(zhí)行所有調(diào)用 dispatch_async 等方法放入main queue 的 block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
/// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件喚醒了,處理這個(gè)事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
} while (1);
/// 10. 通知Observers,即將退出RunLoop
/// 此處有Observer釋放AutoreleasePool: _objc_autoreleasePoolPop();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}
RunLoop 的底層實(shí)現(xiàn)
RunLoop的核心是基于mach port的,其進(jìn)入睡眠時(shí)調(diào)用的函數(shù)時(shí)mach_msg()。
下面稍微介紹一個(gè)OSX/iOS的系統(tǒng)架構(gòu):

應(yīng)用層包括用戶(hù)能接觸到的圖形應(yīng)用,例如 Spotlight、Aqua、SpringBoard 等。
應(yīng)用框架層即開(kāi)發(fā)人員接觸到的 Cocoa 等框架。
核心框架層包括各種核心框架、OpenGL 等內(nèi)容。
Darwin 即操作系統(tǒng)的核心,包括系統(tǒng)內(nèi)核、驅(qū)動(dòng)、Shell 等內(nèi)容,這一層是開(kāi)源的,其所有源碼都可以在 opensource.apple.com 里找到。
我們?cè)谏钊肟匆幌?Darwin 這個(gè)核心的架構(gòu):

XNU是Darwin的內(nèi)核,它是“X is not UNIX”的縮寫(xiě),是一個(gè)混合內(nèi)核,由 Mach微內(nèi)核、 BSD 、IOKit(還包括一些上面沒(méi)標(biāo)注的內(nèi)容)組成。Mach被稱(chēng)作XNU 內(nèi)核的內(nèi)環(huán) ,其作為一個(gè)微內(nèi)核,只能完成操作系統(tǒng)最基本的職責(zé),比如:進(jìn)程和線(xiàn)程、虛擬內(nèi)存管理、任務(wù)調(diào)度、IPC(進(jìn)程通信)和消息傳遞機(jī)制。BSD 層可以看作圍繞 Mach 層的一個(gè)外環(huán),其提供了諸如進(jìn)程管理、文件操作、設(shè)備訪(fǎng)問(wèn)網(wǎng)絡(luò)等功能。IOKit 層是為設(shè)備驅(qū)動(dòng)提供了一個(gè)面向?qū)ο?C++)的一個(gè)框架。
Mach 本身提供的 API 非常有限,而且蘋(píng)果也不鼓勵(lì)使用 Mach 的 API,但是這些API非?;A(chǔ),如果沒(méi)有這些API的話(huà),其他任何工作都無(wú)法實(shí)施。在 Mach 中,所有的東西都是通過(guò)自己的對(duì)象實(shí)現(xiàn)的,進(jìn)程、線(xiàn)程和虛擬內(nèi)存都被稱(chēng)為”對(duì)象”。和其他架構(gòu)不同, Mach 的對(duì)象間不能直接調(diào)用,只能通過(guò)消息傳遞的方式實(shí)現(xiàn)對(duì)象間的通信?!毕ⅰ笔?Mach 中最基礎(chǔ)的概念,消息在兩個(gè)端口 (port) 之間傳遞,這就是 Mach 的 IPC (進(jìn)程間通信) 的核心。
Mach的消息結(jié)構(gòu)如下:
typedef struct{
mach_msg_header_t header;
mach_msg_body_t body;
} mach_msg_base_t;
typedef struct {
mach_msg_bits_t msgh_bits;
mach_msg_size_t msgh_size;
mach_port_t msgh_remote_port;
mach_port_t msgh_local_port;
mach_port_name_t msgh_voucher_port;
mach_msg_id_t msgh_id;
} mach_msg_header_t;
一條 Mach 消息實(shí)際上就是一個(gè)二進(jìn)制數(shù)據(jù)包 (BLOB),其頭部定義了當(dāng)前端口 local_port 和目標(biāo)端口 remote_port,
發(fā)送和接受消息是通過(guò)同一個(gè) API 進(jìn)行的,其 option 標(biāo)記了消息傳遞的方向:
mach_msg_return_t mach_msg_overwrite(
mach_msg_header_t *msg,
mach_msg_option_t option,
mach_msg_size_t send_size,
mach_msg_size_t rcv_size,
mach_port_name_t rcv_name,
mach_msg_timeout_t timeout,
mach_port_name_t notify,
mach_msg_header_t *rcv_msg,
mach_msg_size_t rcv_limit);
為了實(shí)現(xiàn)消息的發(fā)送和接收,mach_msg() 函數(shù)實(shí)際上是調(diào)用了一個(gè) Mach 陷阱 (trap),即函數(shù)mach_msg_trap(),陷阱這個(gè)概念在 Mach 中等同于系統(tǒng)調(diào)用。當(dāng)在用戶(hù)態(tài)調(diào)用 mach_msg_trap() 時(shí)會(huì)觸發(fā)陷阱機(jī)制,切換到內(nèi)核態(tài);內(nèi)核態(tài)中內(nèi)核實(shí)現(xiàn)的 mach_msg() 函數(shù)會(huì)完成實(shí)際的工作,如下圖

這些概念可以參考維基百科: System_call、Trap_(computing)。
RunLoop 的核心就是一個(gè) mach_msg() (見(jiàn)上面代碼的第7步),RunLoop 調(diào)用這個(gè)函數(shù)去接收消息,如果沒(méi)有發(fā)送 port 消息過(guò)來(lái),內(nèi)核會(huì)將線(xiàn)程置于等待狀態(tài)??梢匀我馀芷鹨粋€(gè)app,然后在 App 靜止時(shí)點(diǎn)擊暫停,可以看到主線(xiàn)程調(diào)用棧是停留在 mach_msg_trap() 這個(gè)地方:

關(guān)于具體的如何利用 mach port 發(fā)送信息,可以看看 NSHipster 這一篇文章,或者這里的中文翻譯 。
什么時(shí)候使用RunLoop
IOS的UIApplication和OS X的NSApplication自動(dòng)為主線(xiàn)程創(chuàng)建并啟動(dòng)了runLoop,無(wú)需我們關(guān)心。而其他線(xiàn)程是否需要runLoop需要我們自行判斷。通常情況下,如果只是使用線(xiàn)程執(zhí)行長(zhǎng)時(shí)間且預(yù)置的任務(wù)時(shí)并不需要runLoop。RunLoop更適用于需要與線(xiàn)程進(jìn)行更多交互的情況,例如:
- 使用
ports或自定義輸入源與其它線(xiàn)程進(jìn)行通信. - 使用計(jì)時(shí)器
Timer - Cocoa應(yīng)用程序使用任何
performSelector...方法 -
保持線(xiàn)程以執(zhí)行定期任務(wù)
如果runLoop內(nèi)沒(méi)有source或者repeat執(zhí)行timer等待監(jiān)聽(tīng),則runLoop立即就會(huì)退出銷(xiāo)毀
退出RunLoop
- 傳入超時(shí)時(shí)間
CFRunLoopStop(CFRunLoopRef rl)
定時(shí)器
關(guān)于定時(shí)器,CFRunLoop.c中同時(shí)包含了基于GCD和mk_timer的定時(shí)器,分別用如下宏處理相關(guān)操作:
#if USE_DISPATCH_SOURCE_FOR_TIMERS // GCD
/*處理 GCD timer相關(guān)操作*/
#endif
#if USE_MK_TIMER_TOO // mk_timer
/*處理 mk_timer 相關(guān)操作*/
#endif
在CFRunLoopModeRef中對(duì)應(yīng)不同的timer,成員變量也是不一樣的:
struct __CFRunLoopMode {
#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
};
將 CFRunLoopRef 和 CFRunLoopMode 的 struct 拷貝到代碼中,并添加一個(gè)NSTimer到runLoop中運(yùn)行查看runLoopMode的實(shí)際成員變量值:
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoop {
...
};
struct __CFRunLoopMode {
...
};
- (void)viewDidLoad {
[super viewDidLoad];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3 repeats:YES block:^(NSTimer * _Nonnull timer) {
CFRunLoopRef cfrunLoop = CFRunLoopGetCurrent();
NSLog(@"ddd");
}];
}
斷點(diǎn)查看cfrunloop的_currentMode中成員變量

可見(jiàn),添加到RunLoop的timer只有成員變量_timerPort和_mkTimerArmed,所以NSTimer底層的CFRunLoopTimerRef,是用XUN內(nèi)核的mk_timer創(chuàng)建以及驅(qū)動(dòng)的。
默認(rèn)情況下,timer添加到runLoopMode上標(biāo)記為RunLoopDefaultMode,當(dāng)scrollView滑動(dòng)時(shí),RunLoop的currentMode切換為NSEventTrackingRunLoopMode,此時(shí)計(jì)時(shí)器是不執(zhí)行的(即runLoop的_currentMode不是Timer所關(guān)聯(lián)的mode類(lèi)型,則Timer不執(zhí)行)。
除此之外,當(dāng)某個(gè)預(yù)設(shè)時(shí)間點(diǎn)到達(dá)后,若runLoop正在執(zhí)行一個(gè)長(zhǎng)任務(wù),則該時(shí)間點(diǎn)的回調(diào)被跳過(guò),直到下一個(gè)Loop循環(huán)再執(zhí)行。如果延遲太多以至錯(cuò)過(guò)一個(gè)或多個(gè)Timer預(yù)設(shè)執(zhí)行時(shí)間,則Timer在錯(cuò)過(guò)的時(shí)間段內(nèi)僅回調(diào)執(zhí)行一次。
這就導(dǎo)致了NSTimer并不是實(shí)時(shí)的,無(wú)法保證在非常準(zhǔn)確的時(shí)間點(diǎn)回調(diào)。
關(guān)于這點(diǎn),蘋(píng)果在文檔里也是提到了:
Although it generates time-based notifications, a timer is not a real-time mechanism. Like input sources, timers are associated with specific modes of your run loop. If a timer is not in the mode currently being monitored by the run loop, it does not fire until you run the run loop in one of the timer’s supported modes. Similarly, if a timer fires when the run loop is in the middle of executing a handler routine, the timer waits until the next time through the run loop to invoke its handler routine. If the run loop is not running at all, the timer never fires.
所以在對(duì)計(jì)時(shí)器的精確要求較高的地方
- 應(yīng)當(dāng)將NSTimer添加到標(biāo)記
RunLoopCommonModes的runLoopMode上,標(biāo)記為common的mode會(huì)同步內(nèi)容到runLoop所有的mode上,這樣即使是UITrackingRunLoopMode也會(huì)執(zhí)行timer
[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode]; - 或者使用不受runLoopMode影響的基于GCD的計(jì)時(shí)器:dispatch_source_t timer
PerformSelector
調(diào)用NSObject的performSelector:with:afterDealy:后,實(shí)際上其內(nèi)部會(huì)創(chuàng)建一個(gè)Timer并添加到當(dāng)前線(xiàn)程的RunLoop中,所以如果當(dāng)前線(xiàn)程沒(méi)有RunLoop,則這個(gè)方法會(huì)失效。
override func viewDidLoad() {
super.viewDidLoad()
perform(#selector(sing), with: nil, afterDelay: 3)
}
@objc func sing() {
print("我家住在黃土高坡哦~~~")
}

當(dāng)調(diào)用 performSelector:onThread: 時(shí),實(shí)際上其會(huì)創(chuàng)建一個(gè) Timer 加到對(duì)應(yīng)的線(xiàn)程去,同樣的,如果對(duì)應(yīng)線(xiàn)程沒(méi)有 RunLoop 該方法也會(huì)失效。
App啟動(dòng)后RunLoop的內(nèi)容:
- (void)viewDidLoad {
[super viewDidLoad];
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
NSLog(@"Runloop:{\n %@ \n}",runloop);
}
Runloop:{
current mode = kCFRunLoopDefaultMode
common modes = {
UITrackingRunLoopMode
kCFRunLoopDefaultMode
}
common mode items = {
// Source0
CFRunLoopSource { order = -1, context = { callout = PurpleEventSignalCallback()}}
CFRunLoopSource { order = -2, context = { callout = __handleHIDEventFetcherDrain()}}
CFRunLoopSource { order = -1, context = {callout = __handleEventQueue()}}
CFRunLoopSource { order = 0, context = {callout = FBSSerialQueueRunLoopSourceHandler()}}
// Source1(mach port)
CFRunLoopSource { order = -1, context = { callout = PurpleEventCallback()}}
CFRunLoopSource { order = 0}}
CFRunLoopSource { order = 0}}
CFRunLoopSource { order = 0, context = { callout = _ZL27change_notify_port_callbackP12__CFMachPortPvlS1_()}}
// Observer
CFRunLoopObserver {activities = 0xa0, order = 2001000, callout = _afterCACommitHandler()}
CFRunLoopObserver {activities = 0x20, order = 0, callout = _UIGestureRecognizerUpdateObserver()}
CFRunLoopObserver {activities = 0xa0, order = 1999000, callout = _beforeCACommitHandler()}
CFRunLoopObserver {activities = 0xa0, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler()}
CFRunLoopObserver {activities = 0x1, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler()}
CFRunLoopObserver {activities = 0xa0, order = 2000000, callout = _ZN2CA11Transaction17observer_callbackEP19__CFRunLoopObservermPv()}
},
modes = {
CFRunLoopMode {
name = UIInitializationRunLoopMode,
...
},
CFRunLoopMode {
name = UITrackingRunLoopMode,
sources0 = { /* same as 'common mode items' */},
sources1 = { /* same as 'common mode items' */},
observers = { /* same as 'common mode items' */},
timers = (null),
},
CFRunLoopMode {
name = GSEventReceiveRunLoopMode
sources0 = {
CFRunLoopSource {order = -1, context = CFRunLoopSource context {callout = PurpleEventSignalCallback()}}
},
sources1 = CFBasicHash {
CFRunLoopSource {order = -1, context = CFRunLoopSource context {callout = PurpleEventCallback()}}
},
observers = (null),
timers = (null),
},
CFRunLoopMode {
name = kCFRunLoopDefaultMode
sources0 = { /* same as 'common mode items' */},
sources1 = { /* same as 'common mode items' */},
observers = { /* same as 'common mode items' */},
timers = CFArray {
CFRunLoopTimer {
firing = No, interval = 0, tolerance = 0, next fire date = 555867417 (-13.3055891 @ 4516020992225), callout = (Delayed Perform) UIApplication _accessibilitySetUpQuickSpeak (UIKit.framework/UIKit)})
}},
},
CFRunLoopMode {
name = kCFRunLoopCommonModes,
sources0 = (null),
sources1 = (null),
observers = (null),
timers = (null),
},
}
}
可以看到,系統(tǒng)默認(rèn)注冊(cè)了5個(gè)Mode:
1.UIInitializationRunLoopMode: 在剛啟動(dòng) App 時(shí)第進(jìn)入的第一個(gè) Mode,啟動(dòng)完成后就不再使用。
2.UITrackingRunLoopMode: 界面跟蹤 Mode,用于 ScrollView 追蹤觸摸滑動(dòng),保證界面滑動(dòng)時(shí)不受其他 Mode 影響。
3.GSEventReceiveRunLoopMode: 接受系統(tǒng)事件的內(nèi)部 Mode,通常用不到。
4.kCFRunLoopDefaultMode: App的默認(rèn) Mode,通常主線(xiàn)程是在這個(gè) Mode 下運(yùn)行的。
5.kCFRunLoopCommonModes: 這是一個(gè)占位的 Mode,沒(méi)有實(shí)際作用。
蘋(píng)果只暴露的 DefaultMode 和 CommonMode
你可以在這里看到更多的蘋(píng)果內(nèi)部的 Mode,但那些 Mode 在開(kāi)發(fā)中就很難遇到了
AutoreleasePool
添加到autoreleasePool的對(duì)象會(huì)被延遲釋放,需要等到pool釋放時(shí)才會(huì)對(duì)內(nèi)部保存的對(duì)象發(fā)送release消息。
那Autorelease什么時(shí)候釋放?
上面我們已經(jīng)看到過(guò),App啟動(dòng)后,蘋(píng)果在主線(xiàn)程 RunLoop 里注冊(cè)了兩個(gè) Observer,其回調(diào)都是 _wrapRunLoopWithAutoreleasePoolHandler()。
Runloop:{
...
common mode items = {
CFRunLoopObserver {activities = 0x1, order = -2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler()}
CFRunLoopObserver {activities = 0xa0, order = 2147483647, callout = _wrapRunLoopWithAutoreleasePoolHandler()}
...
},
第一個(gè) Observer的activities = 0x1= kCFRunLoopEntry
監(jiān)視的事件: kCFRunLoopEntry(即將進(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 的activities = 0xa0 = kCFRunLoopBeforeWaiting & kCFRunLoopExit
監(jiān)視了兩個(gè)事件:
1.BeforeWaiting(準(zhǔn)備進(jìn)入休眠) 時(shí)調(diào)用_objc_autoreleasePoolPop() 和 _objc_autoreleasePoolPush() 釋放舊的池并創(chuàng)建新池;
2.Exit(即將退出Loop) 時(shí)調(diào)用 _objc_autoreleasePoolPop() 來(lái)釋放自動(dòng)釋放池。
這個(gè) Observer 的 order 是 2147483647,優(yōu)先級(jí)最低,保證其釋放池子發(fā)生在其他所有回調(diào)之后。
事件響應(yīng)
蘋(píng)果注冊(cè)了一個(gè) Source1 (基于 mach port 的) 用來(lái)接收系統(tǒng)事件,其回調(diào)函數(shù)為__IOHIDEventSystemClientQueueCallback()。
事件的產(chǎn)生和傳遞大致如下:
當(dāng)一個(gè)硬件事件(觸摸/鎖屏/搖晃等)發(fā)生后,由
IOKit.framework生成一個(gè)IOHIDEvent事件
(其中IOKit是蘋(píng)果的硬件驅(qū)動(dòng)框架, 由它進(jìn)行底層接口的抽象封裝與系統(tǒng)進(jìn)行交互傳遞硬件感應(yīng)的事件。它專(zhuān)門(mén)處理用戶(hù)交互設(shè)備,由IOHIDServices和IOHIDDisplays兩部分組成,其中IOHIDServices是專(zhuān)門(mén)處理用戶(hù)交互的,它會(huì)將事件封裝成IOHIDEvents對(duì)象,詳細(xì)請(qǐng)看這里)事件產(chǎn)生后并由 SpringBoard接收。這個(gè)過(guò)程的詳細(xì)情況可以參考這里。SpringBoard 只接收按鍵(鎖屏/靜音等),觸摸,加速,接近傳感器等幾種 Event。
接著用
mach port轉(zhuǎn)發(fā)給需要的App進(jìn)程。隨后IOS注冊(cè)的
Source1接收到IOHIDEvent,在回調(diào)__IOHIDEventSystemClientQueueCallback 內(nèi)觸發(fā)source0,并調(diào)用 _UIApplicationHandleEventQueue() 進(jìn)行應(yīng)用內(nèi)部的分發(fā)。-
_UIApplicationHandleEventQueue()會(huì)把IOHIDEvent處理并包裝成UIEvent進(jìn)行處理或分發(fā),其中包括識(shí)別 UIGesture/處理屏幕旋轉(zhuǎn)/發(fā)送給 UIWindow 等。通常事件比如 UIButton 點(diǎn)擊、touchesBegin/Move/End/Cancel 事件都是在這個(gè)回調(diào)中完成的。觸摸事件的調(diào)用棧如下:
點(diǎn)擊事件的調(diào)用棧
以上為各參考文獻(xiàn)所注,大同小異,然而筆者親自觸發(fā)了一個(gè)觸摸事件后調(diào)用棧的結(jié)構(gòu)如下,只看到了__handleEventQueueInternal和__dispatchPreprocessedEventQueue,而且App啟動(dòng)時(shí)并沒(méi)有觀(guān)察到__IOHIDEventSystemClientQueueCallback()的source1,希望有知道的大佬能為我解惑一下,謝謝!

手勢(shì)識(shí)別
當(dāng)上面的 _UIApplicationHandleEventQueue() 識(shí)別了一個(gè)手勢(shì)時(shí),其首先會(huì)調(diào)用 Cancel 將當(dāng)前的 touchesBegin/Move/End 系列回調(diào)打斷。隨后系統(tǒng)將對(duì)應(yīng)的 UIGestureRecognizer 標(biāo)記為待處理。
蘋(píng)果注冊(cè)了一個(gè) Observer 監(jiān)測(cè) BeforeWaiting (Loop即將進(jìn)入休眠) 事件,這個(gè)Observer的回調(diào)函數(shù)是 _UIGestureRecognizerUpdateObserver(),其內(nèi)部會(huì)獲取所有剛被標(biāo)記為待處理的 GestureRecognizer,并執(zhí)行GestureRecognizer的回調(diào)。
當(dāng)有 UIGestureRecognizer 的變化(創(chuàng)建/銷(xiāo)毀/狀態(tài)改變)時(shí),這個(gè)回調(diào)都會(huì)進(jìn)行相應(yīng)處理。
參考:
iOS線(xiàn)下分享《RunLoop》by 孫源@sunnyxx
深入理解RunLoop
iOS 事件處理機(jī)制與圖像渲染過(guò)
黑幕背后的Autorelease
OS X Internal: A System
