RunLoop原始碼解析
一直計劃著讀讀RunLoop的原始碼,可是一直沒有行動,剛好這個週末沒啥事,就看看RunLoop的原始碼吧。
版本: CF-1151.16原始碼
RunLoop有將近4000行程式碼,每行都解析的話不太現實,這裡就摘抄主要的程式碼。
初識RunLoop
CFRunLoop
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下同時執行。
CFRunLoopMode
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的。
CFRunLoopSource
struct __CFRunLoopSource { CFMutableBagRef _runLoops;// 一個Source 對應多個RunLoop union { CFRunLoopSourceContext version0;// source0 CFRunLoopSourceContext1 version1;//source1 } _context; } // source0 typedef struct { CFIndexversion;// 版本號,用來區分是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; // source1 typedef struct { CFIndexversion;// 版本號 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
。
CFRunLoopObserver
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 };
CFRunLoopTimer
struct __CFRunLoopTimer { CFRunLoopRef _runLoop;// timer 對應的runLoop CFMutableSetRef _rlModes;// 集合,存放對應的modes,猜測一個timer 可以有多個modes,即可以被加入到多個modes中 CFRunLoopTimerCallBack _callout; };
RunLoop( copy、add)函式解析
前面是RunLoop的主要的一些結構體,認識了這些再來看函式就看得明白了。
CFRunLoopCopyCurrentMode
獲取RunLoop正在執行的mode(即_currentMode
)的name。
CFStringRef CFRunLoopCopyCurrentMode(CFRunLoopRef rl) { CFStringRef result = NULL; result = (CFStringRef)CFRetain(rl->_currentMode->_name); return result; }
CFRunLoopCopyAllModes
返回一個數組,其中包含了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新增進陣列array static void __CFRunLoopGetModeName(const void *value, void *context) { CFRunLoopModeRef rlm = (CFRunLoopModeRef)value; CFMutableArrayRef array = (CFMutableArrayRef)context; CFArrayAppendValue(array, rlm->_name); }
CFRunLoopAddCommonMode
向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); } }
CFRunLoopAddSource
新增一個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); } }
CFRunLoopAddObserver
新增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。
CFRunLoopAddTimer
新增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獲取函式解析
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執行函式解析
CFRunLoopRun
預設情況下,運行當前執行緒的RunLoop。
void CFRunLoopRun(void) { int32_t result; do { result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false); } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result); }
kCFRunLoopDefaultMode
CFRunLoopRunInMode
在指定的mode下運行當前執行緒的RunLoop
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled); }
該方法,可以設定runLoop執行在哪個mode下modeName
,超時時間seconds
,以及是否處理完事件就返回returnAfterSourceHandled
。
這兩個方法實際呼叫的是同一個方法CFRunLoopRunSpecific
,其返回是一個SInt32
型別的值,根據返回值,來決定RunLoop的執行狀況。
CFRunLoopRunSpecific
在指定的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; }
- kCFRunLoopRunFinished mode中沒有事件處理,直接返回;
- kCFRunLoopEntry RunLoop即將開始執行,通知observers;
- kCFRunLoopExit RunLoop 即將退出,通知observers。
__CFRunLoopRun
這裡處理了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 <= 0.0) { // instant timeout seconds = 0.0; timeout_context->termTSR = 0ULL; // timer2 即將超時 } 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_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout); dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel); dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL); // 定時器執行 dispatch_resume(timeout_timer); } else { // timer3 永不超時 seconds = 9999999999.0; timeout_context->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
四種列舉型別。
總結
RunLoop執行核心就是一個do..while迴圈,遍歷所有事件,有事件處理,無事件休眠,直至達到退出條件。