為了文章簡潔,只摘抄部分主要的代碼
源碼詳細介紹在這裡

應該是一個美麗的下午,在一場面試上,遇見了runLoop,可惜擦肩而過。。。
struct __CFRunLoop {
pthread_t _pthread; // runLoop 對應的線程
__CFPort _wakeUpPort; // 用來喚醒runLoop的端口,接收消息,執行CFRunLoopWakeUp方法
CFMutableSetRef _commonModes; // 集合,所有標記為common的mode的集合
CFMutableSetRef _commonModeItems; // 集合,commonMode的item(observers/sources/timers)的集合
CFRunLoopModeRef _currentMode; // 當前runLoop運行的mode
CFMutableSetRef _modes; // 集合,mode的集合};從源碼可以看出一部分內容 :
一個runLoop對象,主要包含一個線程_pthread,一個用來被喚醒的端口_wakeUpPort,一個當前運行的mode_currentMode,以及若干個_modes、_commonModes、_commonModeItems。
runLoop有很多mode,即_modes,但是只有一個_currentMode,runLoop一次只能運行在一個mode下,不可能在多個mode下同時運行。
struct __CFRunLoopMode { CFStringRef _name; // mode的名字,唯一標識
Boolean _stopped; // mode的狀態,是否停止
CFMutableSetRef _sources0; // sources0 的集合
CFMutableSetRef _sources1; // sources1 的集合
CFMutableArrayRef _observers; // 存儲所有觀察者(observers)的數組
CFMutableArrayRef _timers; // 存儲所有定時器(timers)的數組
// 源碼中有一段代碼,可以看出字典的存儲對象
// CFDictionarySetValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port, rls);
CFMutableDictionaryRef _portToV1SourceMap; // 字典 key是__CFPort,value是CFRunLoopSourceRef
// __CFPortSetInsert(src_port, rlm->_portSet);
__CFPortSet _portSet; // 端口的集合}從mode的組成可以看出來:mode管理了所有的事件(sources/timers/observers),而runLoop是管理mode的
struct __CFRunLoopSource { CFMutableBagRef _runLoops; // 一個Source 對應多個runLoop
union { CFRunLoopSourceContext version0; // source0
CFRunLoopSourceContext1 version1; //source1
} _context;
}// source0typedef struct { CFIndex version; // 版本號,用來區分是source1還是source0
void * info; // schedule cancel 是對應的,
void (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode); void (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode); void (*perform)(void *info); // 用來回調的指針} CFRunLoopSourceContext;// source1typedef struct { CFIndex version; // 版本號
void * info;#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)) || (TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
mach_port_t (*getPort)(void *info); // 端口
void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);#else
void * (*getPort)(void *info); void (*perform)(void *info); // 用來回調的指針#endif} CFRunLoopSourceContext1;源碼中看出來,source0和source1的區別,source1比source0多一個接收消息的端口mach_port_t
struct __CFRunLoopObserver { CFRunLoopRef _runLoop; // observer對應的runLoop, 一一對應
CFIndex _rlCount; // observer當前監測的runLoop數量,主要在安排/移除runLoop的時候用到
CFOptionFlags _activities; // observer觀測runLoop的狀態,枚舉類型,
CFIndex _order; // mode使用數組存儲observers,根據_order添加observer
CFRunLoopObserverCallBack _callout;
};_activities狀態值:
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 即將進入Loop
kCFRunLoopBeforeTimers = (1UL << 1), // runLoop即將處理 Timers
kCFRunLoopBeforeSources = (1UL << 2), // runLoop即將處理 Sources
kCFRunLoopBeforeWaiting = (1UL << 5), // runLoop即將進入休眠
kCFRunLoopAfterWaiting = (1UL << 6), // runLoop剛從休眠中喚醒
kCFRunLoopExit = (1UL << 7), // 即將退出RunLoop
kCFRunLoopAllActivities = 0x0FFFFFFFU
};struct __CFRunLoopTimer { CFRunLoopRef _runLoop; // timer 對應的runLoop
CFMutableSetRef _rlModes; // 集合,存放對應的modes,猜測一個timer 可以有多個modes,即可以被加入到多個modes中
CFRunLoopTimerCallBack _callout;
};5個類之間的主要方法,來詳細了解類之間的相互關系
獲取runLoop正在運行的mode(即
_currentMode)的name。
CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl) { CFStringRef result = NULL;
result = (CFStringRef)CFRetain(rl->_currentMode->_name); return result;
}返回一個數組,其中包含了runLoop所有定義過的mode(即
_modes)的name
CFArrayRef CFRunLoopCopyAllModes(CFRunLoopRef rl) { CFMutableArrayRef array;
array = CFArrayCreateMutable(kCFAllocatorSystemDefault, CFSetGetCount(rl->_modes), &kCFTypeArrayCallBacks); // CFSetApplyFunction 三個參數a,b,c,
// 表示:對a裡面的每個對象,都執行一次b方法,b方法的參數是a和c,後面會多次遇到
CFSetApplyFunction(rl->_modes, (__CFRunLoopGetModeName), array); return array;
} // 把mode的name添加進數組arraystatic void __CFRunLoopGetModeName(const void *value, void *context) { CFRunLoopModeRef rlm = (CFRunLoopModeRef)value; CFMutableArrayRef array = (CFMutableArrayRef)context; CFArrayAppendValue(array, rlm->_name);
}向runLoop的commonModes添加一個mode
void CFRunLoopAddCommonMode(CFRunLoopRef rl, CFStringRef modeName) { // 判斷 modeName 是否在_commonModes 中,如果已經存在,else中不做任何處理
if (!CFSetContainsValue(rl->_commonModes, modeName)) { // set 是 runLoop 的 _commonModeItems一份拷貝
CFSetRef set = rl->_commonModeItems ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModeItems) : NULL; // 1. _commonModes 添加 modeName,
// 可見_commonModes存儲的其實是CFStringRef類型的modeName
CFSetAddValue(rl->_commonModes, modeName); // 如果items 存在
if (NULL != set) { CFTypeRef context[2] = {rl, modeName}; // 2. 為modeName對應的Mode添加items中的每個item(timer/source/observer)
// 為set中的每個item,調用一次__CFRunLoopAddItemsToCommonMode方法
CFSetApplyFunction(set, (__CFRunLoopAddItemsToCommonMode), (void *)context);
}
} else {
}
} // 把一個item添加到指定的mode中static void __CFRunLoopAddItemsToCommonMode(const void *value, void *ctx) { CFTypeRef item = (CFTypeRef)value; CFRunLoopRef rl = ()(((CFTypeRef *)ctx)[0]); CFStringRef modeName = (CFStringRef)(((CFTypeRef *)ctx)[1]); // 判斷item具體是哪種類型,然後進行添加
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);
}
}添加一個source到指定的runLoopMode
void CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef rls, CFStringRef modeName) { /* DOES CALLOUT */
// 聲明一個bool值的標識,後續用來source0 添加source
Boolean doVer0Callout = false; // 1. 如果是commonMode,那麼commonModes中的所有mode都要更新
if (modeName == kCFRunLoopCommonModes) { /*
這裡獲取rl->_commonModes並賦值set,如果沒有為NULL
同時獲取rl->_commonModeItems,如果不存在就初始化創建
*/
// 1.1 先把 rls 添加進_commonModeItems
CFSetAddValue(rl->_commonModeItems, rls); // 1.2 為set中其他的mode,添加rls
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
} // 2. 非commonMode的添加
else { // 2.1 在runLoop的_modes中查找名字為modeName的mode,找不到會在內部進行初始化創建(true決定是否創建)
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true); // 2.2 獲取mode的跟source有關的_sources0,_sources1以及端口_portToV1SourceMap
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);
} // 2.3 判斷rls屬於哪種類型,並針對性的添加
// 2.3.1 source0的情況
if (0 == rls->_context.version0.version) { CFSetAddValue(rlm->_sources0, rls); // 下面這段代碼是後面的,放在這裡便於理解,source0 有個schedule指針,把rl和rlm關聯起來
rls->_context.version0.schedule(rls->_context.version0.info, rl, modeName);
} // 2.3.2 source1的情況
else if (1 == rls->_context.version0.version) { CFSetAddValue(rlm->_sources1, rls); // 獲取rls的端口
__CFPort src_port = rls->_context.version1.getPort(rls->_context.version1.info); // rls和端口一一對應,並存儲在mode的字典_portToV1SourceMap中
CFDictionarySetValue(rlm->_portToV1SourceMap, (const void *)(uintptr_t)src_port, rls); // 把source1 的端口添加進mode的端口集合_portSet中
__CFPortSetInsert(src_port, rlm->_portSet);
} // 2.4 把rl 加入到rls的_runLoops中,即一個resources可以對應多個runLoop
if (NULL == rls->_runLoops) {
rls->_runLoops = CFBagCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeBagCallBacks); // sources retain run loops!
} CFBagAddValue(rls->_runLoops, rl);
}
}添加rlo到指定的rlm
CF_EXPORT void CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFRunLoopMode mode);
內部實現CFRunLoopSource跟差不多,都是根據mode是否commonMode分兩種情況,差別在於:
關聯mode:mode有一個數組_observers,添加是根據rlo的_order進行添加的
關聯rl:根據_rlCount是否為0。只有當rlo的_rlCount為0時,其_runLoop才是rl。
添加rlt到指定的rlm
CF_EXPORT void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFRunLoopMode mode);
內部實現同上,區別:
rlt只能添加到其_runLoop的mode中,如果rl不是其_runLoop,直接返回
if (NULL == rlt->_runLoop) {
rlt->_runLoop = rl;
} else if (rl != rlt->_runLoop) {
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl); return;
}rlt有一個變量_rlModes,其存儲的是rlt所在的mode的name
CFSetAddValue(rlt->_rlModes, rlm->_name);
rlm有一個變量_timers,其存儲timer是根據timer的啟動時間,即_fireTSR,進行排序的
runLoop跟其所在線程是一一對應的
API提供了兩個獲取runLoop的方法
CFRunLoopRef CFRunLoopGetMain(void) { static CFRunLoopRef __main = NULL; // no retain needed
// pthread_main_thread_np() 主線程
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}CFRunLoopRef CFRunLoopGetCurrent(void) { CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop); if (rl) return rl; // pthread_self() 當前線程
return _CFRunLoopGet0(pthread_self());
}其中,TSD是thread special data,表示線程私有數據,在 C++ 中,全局變量可以被所有線程訪問,局部變量只有函數內部可以訪問。而 TSD 的作用就是能夠在同一個線程的不同函數中被訪問。(找到的資料)__CFTSDKeyRunLoop是一個枚舉類型的關鍵字。pthread_self()可以得知,如果要獲取非主線程的runLoop,必須在該線程內部調用CFRunLoopGetCurrent才能獲取。
根據線程t獲取對應的runLoop
// 一個內部全局的字典static CFMutableDictionaryRef __CFRunLoops = NULL;CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) { // 1. 保證t不為空
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
} // 2. 創建全局字典,並存儲主線程的runLoop
if (!__CFRunLoops) { CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks); // 通過pthread_main_thread_np()創建CFRunLoopRef類型的mainLoop,內部對其所有變量進行初始化,並且賦值_pthread為pthread_main_thread_np()
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np()); // key是主線程的指針, value 是剛創建的mainLoop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop); // 比較並交換指針,
// 這裡比較第一個參數NULL和第三個參數 (void * volatile *)&__CFRunLoops全局字典,如果相等,系統會自動把第二參數的值賦給第三個參數,
// volatile的作用是 每次取得數值的方式是直接從內存中讀取
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) { CFRelease(dict);
} // coreFoundation 要手動管理內存, create 對應 release
CFRelease(mainLoop);
} // 3. 全局字典已經存在,從中獲取對應線程t的runLoop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); // 如果獲取不到loop,
if (!loop) { // 根據 t 創建 一個newLoop
CFRunLoopRef newLoop = __CFRunLoopCreate(t); // 再一次進行獲取
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t)); // 如果還不存在,就直接賦值,
if (!loop) { CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
} // 4. 注冊TSD
if (pthread_equal(t, pthread_self())) { // 注冊回調,當線程銷毀時,順便也銷毀其對應的 RunLoop
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL); if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
} return loop;
}線程和runLoop是一一對應,保存在一個全局字典裡,主線程的runLoop是在初始化字典時已經創建好了,其他線程的runLoop只有在獲取的時候才會創建。
默認情況下,運行當前線程的runLoop
void CFRunLoopRun(void) {
int32_t result; do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}源碼得知:
kCFRunLoopDefaultMode,默認情況下,runLoop是在這個mode下運行的,
runLoop的運行主體是一個do..while循環,除非停止或者結束,否則runLoop會一直運行下去
在指定的mode下運行當前線程的runLoop
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}該方法,可以設置runLoop運行在哪個mode下modeName,超時時間seconds,以及是否處理完事件就返回returnAfterSourceHandled。
這兩個方法實際調用的是同一個方法CFRunLoopRunSpecific,其返回是一個SInt32類型的值,根據返回值,來決定runLoop的運行狀況。
在指定的mode下,運行指定的runLoop
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) {
// 根據rl,modeName獲取指定的currentMode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false); // 1. 如果當前mode 不存在,或者當前mode中事件為空,runLoop 結束,返回 kCFRunLoopRunFinished
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) { // 聲明一個標識did,默認false
Boolean did = false; // did 為 false,返回 kCFRunLoopRunFinished
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
} // 初始化一個返回結果,值為kCFRunLoopRunFinished
int32_t result = kCFRunLoopRunFinished; // 2. kCFRunLoopEntry, 通知observers 即將開始循環
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry); // runLoop運行主體
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode); // 3. kCFRunLoopExit, 通知 observers 即將退出循環runLoop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit); return result;
}這裡有3點:
kCFRunLoopRunFinished mode中沒有事件處理,直接返回
kCFRunLoopEntry runLoop即將開始運行,通知observers
kCFRunLoopExit runLoop 即將退出,通知observers
這裡處理了runLoop從開始運行到退出的所有邏輯
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) { // 1. 如果runLoop停止或者runLoopMode為停止狀態,直接返回 kCFRunLoopRunStopped
if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl); return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false; return kCFRunLoopRunStopped;
} // 獲取主線程用來接收消息的端口
dispatchPort = _dispatch_get_main_queue_port_4CF(); // 獲取執行timers對應的線程的端口
modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue); // GCD 管理的定時器,用於實現runLoop的超時機制
dispatch_source_t timeout_timer = NULL;
struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context)); // 處理timer 三種情況 :timer1 立即超時
if (seconds termTSR = 0ULL; // timer2 即將超時
} else if (seconds termTSR = UINT64_MAX;
} // 聲明一個標識,默認true,用於執行消息處理
Boolean didDispatchPortLastTime = true; // 聲明一個返回值,用於最後的結果返回
int32_t retVal = 0; // do..while循環主體,處理runLoop的邏輯
do { // 獲取rlm的端口集合
__CFPortSet waitSet = rlm->_portSet; // runLoop設置為可被喚醒的狀態
__CFRunLoopUnsetIgnoreWakeUps(rl); // 2. kCFRunLoopBeforeTimers runLoop即將處理Timers, 通知observers
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers); // 3. kCFRunLoopBeforeSources runLoop即將處理Sources,通知observers
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources); // 4. runLoop開始處理source0事件
// sourceHandledThisLoop 是否處理完Source0事件
// 內部實現是,只有被標記Signaled的source0事件才會被處理,但在處理之前會去除標記__CFRunLoopSourceUnsetSignaled
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle); if (sourceHandledThisLoop) { // 處理完Source0之後的回調
__CFRunLoopDoBlocks(rl, rlm);
} // 處理完source0事件,且沒有超時 poll 為false,
// 沒有處理完source0 事件,或者超時,為true
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR); // didDispatchPortLastTime 初始化為true,即第一次循環的時候不會走if方法,
// 5. 消息處理,source1 事件,goto 第9步
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) { // 從消息緩沖區獲取消息
msg = (mach_msg_header_t *)msg_buffer; // dispatchPort收到消息,立刻去處理
// dispatchPort 主線程接收消息的端口
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) { // 收到消息,立馬去處理
goto handle_msg;
} if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) { goto handle_msg;
}
} // didDispatchPortLastTime 設置為false,以便進行消息處理
didDispatchPortLastTime = false; // 6. kCFRunLoopBeforeWaiting,通知 observers runLoop即將休眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting); // runLoop 休眠
__CFRunLoopSetSleeping(rl); // 7.線程進入休眠, 直到被下面某一個事件喚醒。(文檔給出的結果:)
// 7.1. 基於 port 的Source1 的事件
// 7.2. Timer 到時間了
// 7.3. RunLoop 啟動時設置的最大超時時間到了
// 7.4. 被手動喚醒
do { // 從消息緩沖區獲取消息
msg = (mach_msg_header_t *)msg_buffer; // 內部調用 mach_msg() 等待接受 waitSet 的消息
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
} while (1); // 設置rl不再等待喚醒
__CFRunLoopSetIgnoreWakeUps(rl); // runloop 醒來
__CFRunLoopUnsetSleeping(rl); // 8. kCFRunLoopAfterWaiting 已被喚醒,通知observers
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting); // 9. 處理消息
handle_msg:; // 設置rl不再等待喚醒
__CFRunLoopSetIgnoreWakeUps(rl); // 判斷 livePort
// 9.1 如果不存在
if (MACH_PORT_NULL == livePort) { CFRUNLOOP_WAKEUP_FOR_NOTHING(); // 9.2 如果是喚醒rl的端口,回到第2步
} else if (livePort == rl->_wakeUpPort) { CFRUNLOOP_WAKEUP_FOR_WAKEUP();
ResetEvent(rl->_wakeUpPort);
} // 定時器事件__CFRunLoopDoTimers
// 9.3 如果是定時器的端口
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) { // 處理定時器事件
CFRUNLOOP_WAKEUP_FOR_TIMER(); if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
__CFArmNextTimerInMode(rlm, rl);
}
} // 9.4. 如果端口是主線程的端口,直接處理
else if (livePort == dispatchPort) { CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else { // 9.5. 除上述4點之外的端口
CFRUNLOOP_WAKEUP_FOR_SOURCE(); // 從端口收到的消息事件,為source1事件
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort); if (rls) {
mach_msg_header_t *reply = NULL; // 處理source1 事件
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop; if (NULL != reply) { // 消息處理,
// message.h中,以後有時間會再研究一下
(void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
}
}
}
// 10. 返回結果的處理
if (sourceHandledThisLoop && stopAfterHandle) { // 10.1 如果事件處理完就返回,並且source處理完成
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) { // 10.2 超時
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) { // 10.3 被外部調用者強制停止了
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (rlm->_stopped) { // 10.4 runLoopMode 狀態停止
rlm->_stopped = false;
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) { // 10.5 source/timer/observer一個都沒有了
retVal = kCFRunLoopRunFinished;
} // 上述幾種情況,會跳出do..while循環,
// 除此之外,繼續循環
} while (0 == retVal); return retVal;
}上述2-10就是runLoop運行過程中的循環邏輯,而最終返回的狀態有:kCFRunLoopRunFinished、kCFRunLoopRunStopped、kCFRunLoopRunTimedOut以及kCFRunLoopRunHandledSource四種枚舉類型
1. runLoop跟線程一一對應,非主線程的rl只能在其內部獲取,runLoop管理rlm和回調block,而rlm存儲了所有的事件。
2. runLoop運行核心就是一個do..while循環,遍歷所有事件,有事件處理,無事件休眠,直至達到退出條件。
3. 以上就是runLoop內部的源碼分析,當然會有理解不到位的情況,也留有待解決的問題,萬望不吝賜教。
參考資料:
深入理解RunLoop
RunLoop系列之源碼分析
Run Loops
CFRunLoop