前言
文章主要會(huì)RunLoop源碼進(jìn)行剖析,里面會(huì)有對(duì)它的理解及注釋,有不足望見(jiàn)解
1,RunLoop是什么?
廣義上的來(lái)說(shuō),run loop 就是所謂的?event loop,或者稱(chēng)之為「事件循環(huán)」或者「事件分發(fā)器」。Event loop 是?event-driven programming(事件驅(qū)動(dòng)編程)非常重要的組成部分,而事件驅(qū)動(dòng)編程則是 GUI 程序的最常見(jiàn)編程方式(現(xiàn)在似乎在服務(wù)器端也有很多應(yīng)用榛瓮,但在 GUI 編程方面肯定是繞不過(guò)去)。
Event Loop
Run Loop 是一個(gè) iOS 開(kāi)發(fā)里的基礎(chǔ)概念,它并非獨(dú)有的機(jī)制嗤栓,很多系統(tǒng)和框架都有類(lèi)似的實(shí)現(xiàn),Run Loop 是 Event Loop (事件循環(huán))機(jī)制的在 iOS 平臺(tái)的一種實(shí)現(xiàn)箍邮。
Event loop 的思想非常簡(jiǎn)單茉帅,用下面的偽代碼來(lái)表示:
'hello'
int main(void){
? ? 初始化();
? ? while(message !=退出){
? ? ? ? 處理事件(message);
? ? ? ? message =獲取下一個(gè)事件();
? ? }
? ? return 0;
}
```
蘋(píng)果官網(wǎng)文檔:https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Multithreading/RunLoopManagement/RunLoopManagement.html
Run loops are part of the fundamental infrastructure associated with threads. A?run loop?is an event processing loop that you use to schedule work and coordinate the receipt of incoming events. The purpose of a run loop is to keep your thread busy when there is work to do and put your thread to sleep when there is none.
Run loop management is not entirely automatic. You must still design your thread’s code to start the run loop at appropriate times and respond to incoming events. Both Cocoa and Core Foundation provide?run loop objects?to help you configure and manage your thread’s run loop. Your application does not need to create these objects explicitly; each thread, including the application’s main thread, has an associated run loop object. Only secondary threads need to run their run loop explicitly, however. The app frameworks automatically set up and run the run loop on the main thread as part of the application startup process.
The following sections provide more information about run loops and how you configure them for your application. For additional information about run loop objects, see?NSRunLoop Class Reference?and?CFRunLoop Reference.
運(yùn)行循環(huán)是與線程相關(guān)的基礎(chǔ)架構(gòu)的一部分。一個(gè)運(yùn)行循環(huán)是指用于安排工作锭弊,并協(xié)調(diào)接收傳入事件的事件處理循環(huán)堪澎。運(yùn)行循環(huán)的目的是在有工作時(shí)保持線程忙,并在沒(méi)有線程時(shí)讓線程進(jìn)入休眠狀態(tài)味滞。
運(yùn)行循環(huán)管理不是完全自動(dòng)的樱蛤。您仍然必須設(shè)計(jì)線程的代碼以在適當(dāng)?shù)臅r(shí)間啟動(dòng)運(yùn)行循環(huán)并響應(yīng)傳入的事件马昙。Cocoa和Core Foundation都提供了運(yùn)行循環(huán)對(duì)象來(lái)幫助您配置和管理線程的運(yùn)行循環(huán)。您的應(yīng)用程序不需要顯式創(chuàng)建這些對(duì)象;?每個(gè)線程(包括應(yīng)用程序的主線程)都有一個(gè)關(guān)聯(lián)的運(yùn)行循環(huán)對(duì)象刹悴。但是行楞,只有輔助線程需要顯式運(yùn)行其運(yùn)行循環(huán)。作為應(yīng)用程序啟動(dòng)過(guò)程的一部分土匀,應(yīng)用程序框架會(huì)自動(dòng)在主線程上設(shè)置并運(yùn)行運(yùn)行循環(huán)子房。
以下部分提供有關(guān)運(yùn)行循環(huán)以及如何為應(yīng)用程序配置它們的更多信息。有關(guān)運(yùn)行循環(huán)對(duì)象的其他信息就轧,請(qǐng)參閱NSRunLoop類(lèi)參考和CFRunLoop參考证杭。
Figure 3-1?shows the conceptual structure of a run loop and a variety of sources. The input sources deliver asynchronous events to the corresponding handlers and cause the?runUntilDate:?method (called on the thread’s associated?NSRunLoop?object) to exit. Timer sources deliver events to their handler routines but do not cause the run loop to exit.
圖3-1???運(yùn)行循環(huán)的結(jié)構(gòu)及其來(lái)源
回到 macOS/iOS 平臺(tái)上,對(duì)于 event loop 的具體實(shí)現(xiàn)有兩個(gè):
Foundation 框架中的?NSRunLoop
Core Foundation 框架中的?CFRunLoop
其中?NSRunLoop?是對(duì)?CFRunLoop?的簡(jiǎn)單封裝妒御,需要著重研究的只有?CFRunLoop解愤。
2,Run Loop 實(shí)現(xiàn)
網(wǎng)上目前有關(guān) Run Loop 的文章, 10 篇里面可能有 8 篇都是重復(fù)了?深入理解RunLoop?中的代碼乎莉。包括本人閱讀不下5遍,在閱讀runloop源碼前后都去閱讀YY作者文章,然而這都是經(jīng)過(guò)作者大量簡(jiǎn)化過(guò)的版本送讲,隱藏了大量的細(xì)節(jié)。
RunLoop 與線程的關(guān)系
首先惋啃,iOS 開(kāi)發(fā)中能遇到兩個(gè)線程對(duì)象: pthread_t 和 NSThread哼鬓。過(guò)去蘋(píng)果有份文檔標(biāo)明了 NSThread 只是 pthread_t 的封裝,但那份文檔已經(jīng)失效了边灭,現(xiàn)在它們也有可能都是直接包裝自最底層的 mach thread异希。蘋(píng)果并沒(méi)有提供這兩個(gè)對(duì)象相互轉(zhuǎn)換的接口,但不管怎么樣绒瘦,可以肯定的是 pthread_t 和 NSThread 是一一對(duì)應(yīng)的称簿。比如,你可以通過(guò) pthread_main_thread_np() 或 [NSThread mainThread] 來(lái)獲取主線程惰帽;也可以通過(guò) pthread_self() 或 [NSThread currentThread] 來(lái)獲取當(dāng)前線程憨降。CFRunLoop 是基于 pthread 來(lái)管理的。
蘋(píng)果不允許直接創(chuàng)建 RunLoop善茎,它只提供了兩個(gè)自動(dòng)獲取的函數(shù):CFRunLoopGetMain() 和 CFRunLoopGetCurrent()券册。 這兩個(gè)函數(shù)內(nèi)部的邏輯大概是下面這樣:
'''
CFRunLoopGetMain :
CFRunLoopRefCFRunLoopGetMain(void) {
? ? CHECK_FOR_FORK();
? ? static CFRunLoopRef __main = NULL; // no retain needed
? ? if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
? ? return__main;
}
CFRunLoopRefCFRunLoopGetCurrent(void) {
? ? CHECK_FOR_FORK();
? ? CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
? ? if(rl)returnrl;
? ? return _CFRunLoopGet0(pthread_self());
}
'''
CFRunLoopGet0
無(wú)論是 CFRunLoopGetMain 還是 CFRunLoopGetCurrent ,兩者調(diào)用了 CFRunLoopGet0 :
// 全局的Dictionary垂涯,key 是 pthread_t烁焙, value 是 CFRunLoopRef
'''
static CFMutableDictionaryRef __CFRunLoops = NULL;
/// 訪問(wèn) loopsDic 時(shí)的鎖
staticCFLock_t loopsLock = CFLockInit;
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
/// 獲取一個(gè) pthread 對(duì)應(yīng)的 RunLoop。
CF_EXPORTCFRunLoopRef_CFRunLoopGet0(pthread_tt) {
? ? if(pthread_equal(t,kNilPthreadT)) {
t =pthread_main_thread_np();
? ? }
? ? __CFLock(&loopsLock);
? ? if (!__CFRunLoops) {
?? ? ? ? // 第一次進(jìn)入時(shí)耕赘,初始化全局Dic骄蝇,并先為主線程創(chuàng)建一個(gè) RunLoop。
? ? ? ? __CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict,pthreadPointer(pthread_main_thread_np()), mainLoop);
if(!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void*volatile*)&__CFRunLoops)) {
? ? CFRelease(dict);
}
CFRelease(mainLoop);
? ? ? ? __CFLock(&loopsLock);
? ? }
? ? /// 直接從 Dictionary 里獲取操骡。
? ? CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
? ? __CFUnlock(&loopsLock);
? ? if(!loop) {
? ? ? ? //直接創(chuàng)建一個(gè)
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
? ? ? ? __CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if(!loop) {
? ? ? ? /// 取不到時(shí)九火,創(chuàng)建一個(gè)
? ? 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)) {
?? ? ? ? ? ? ? // 注冊(cè)一個(gè)回調(diào)赚窃,當(dāng)線程銷(xiāo)毀時(shí),順便也銷(xiāo)毀其對(duì)應(yīng)的 RunLoop岔激。
? ? ? ? ? ? _CFSetTSD(__CFTSDKeyRunLoopCntr, (void*)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void(*)(void*))__CFFinalizeRunLoop);
? ? ? ? }
? ? }
? ? returnloop;
}
'''
從上面的代碼可以看出勒极,線程和 RunLoop 之間是一一對(duì)應(yīng)的,其關(guān)系是保存在一個(gè)全局的 Dictionary 里虑鼎。線程剛創(chuàng)建時(shí)并沒(méi)有 RunLoop辱匿,如果你不主動(dòng)獲取,那它一直都不會(huì)有炫彩。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí)匾七,RunLoop 的銷(xiāo)毀是發(fā)生在線程結(jié)束時(shí)。你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop(主線程除外)江兢。
CHECK_FOR_FORK()
在兩個(gè)函數(shù)里昨忆,都有使用了 CHECK_FOR_FORK() 。
它應(yīng)該是屬于多進(jìn)程情況下的一個(gè)斷言杉允。
Threading Programming Guide?中邑贴,有這么一段話(huà):
Warning: When launching separate processes using the fork function, you must always follow a call to fork with a call to exec or a similar function. Applications that depend on the Core Foundation, Cocoa, or Core Data frameworks (either explicitly or implicitly) must make a subsequent call to an exec function or those frameworks may behave improperly.
也就是說(shuō),當(dāng)通過(guò) fork 啟動(dòng)一個(gè)新進(jìn)程的時(shí)候夺颤,你必須要接著調(diào)用一個(gè) exec 或類(lèi)似的函數(shù)痢缎。而依賴(lài)于 Core Founadtion / Cocoa / Core Data 框架的應(yīng)用,必須調(diào)用 exec 函數(shù),否則這些框架也許不能正確的工作胁勺。
所以為了保證安全世澜,使用 CHECK_FOR_FORK 進(jìn)行檢查。
FORK
這里簡(jiǎn)單提一下 fork 署穗。
在 UNIX 中寥裂,用 fork 來(lái)創(chuàng)建子進(jìn)程,調(diào)用 fork( ) 的進(jìn)程被稱(chēng)為父進(jìn)程案疲,新進(jìn)程是子進(jìn)程封恰,并且?guī)缀跏歉高M(jìn)程的完全復(fù)制(變量、文件句柄褐啡、共享內(nèi)存消息等相同诺舔,但 process id 不同)。
因?yàn)樽舆M(jìn)程和父進(jìn)程基本是一樣的备畦,要想讓子進(jìn)程去執(zhí)行其他不同的程序低飒,子進(jìn)程就需要調(diào)用 exec ,把自身替換為新的進(jìn)程懂盐,其中process id不變褥赊,但原來(lái)進(jìn)程的代碼段、堆棧段莉恼、數(shù)據(jù)段被新的內(nèi)容取代拌喉,來(lái)執(zhí)行新的程序速那。
這樣 fork 和 exec 就成為一種組合。
而在 iOS 這樣的類(lèi) UNIX 系統(tǒng)里尿背,基本上也都要通過(guò) fork 的形式來(lái)創(chuàng)建新的進(jìn)程端仰。
假如沒(méi)有執(zhí)行完 exec ,那么執(zhí)行的代碼段等內(nèi)容田藐,還是父進(jìn)程里的榆俺,出現(xiàn)問(wèn)題可以說(shuō)百分之百。這就是 CHECK_FOR_FORK 檢查的目的坞淮。
RunLoop 對(duì)外的接口
在 CoreFoundation 里面關(guān)于 RunLoop 有5個(gè)類(lèi):
CFRunLoopRef
CFRunLoopModeRef
CFRunLoopSourceRef
CFRunLoopTimerRef
CFRunLoopObserverRef
其中 CFRunLoopModeRef 類(lèi)并沒(méi)有對(duì)外暴露茴晋,只是通過(guò) CFRunLoopRef 的接口進(jìn)行了封裝。他們的關(guān)系如下:
一個(gè) RunLoop 包含若干個(gè) Mode回窘,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer诺擅。每次調(diào)用 RunLoop 的主函數(shù)時(shí),只能指定其中一個(gè) Mode啡直,這個(gè)Mode被稱(chēng)作 CurrentMode烁涌。如果需要切換 Mode,只能退出 Loop酒觅,再重新指定一個(gè) Mode 進(jìn)入撮执。這樣做主要是為了分隔開(kāi)不同組的 Source/Timer/Observer,讓其互不影響舷丹。
CFRunLoopSourceRef?是事件產(chǎn)生的地方抒钱。Source有兩個(gè)版本:Source0 和 Source1。
? Source0 只包含了一個(gè)回調(diào)(函數(shù)指針)颜凯,它并不能主動(dòng)觸發(fā)事件谋币。使用時(shí),你需要先調(diào)用 CFRunLoopSourceSignal(source)症概,將這個(gè) Source 標(biāo)記為待處理蕾额,然后手動(dòng)調(diào)用 CFRunLoopWakeUp(runloop) 來(lái)喚醒 RunLoop,讓其處理這個(gè)事件彼城。
? Source1 包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針)诅蝶,被用于通過(guò)內(nèi)核和其他線程相互發(fā)送消息。這種 Source 能主動(dòng)喚醒 RunLoop 的線程募壕,其原理在下面會(huì)講到调炬。
CFRunLoopTimerRef?是基于時(shí)間的觸發(fā)器,它和 NSTimer 是toll-free bridged 的司抱,可以混用筐眷。其包含一個(gè)時(shí)間長(zhǎng)度和一個(gè)回調(diào)(函數(shù)指針)。當(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)。
CFRunLoopObserverRef?是觀察者武翎,每個(gè) Observer 都包含了一個(gè)回調(diào)(函數(shù)指針)烈炭,當(dāng) RunLoop 的狀態(tài)發(fā)生變化時(shí),觀察者就能通過(guò)回調(diào)接受到這個(gè)變化宝恶》叮可以觀測(cè)的時(shí)間點(diǎn)有以下幾個(gè):
'''
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
};
'''
上面的 Source/Timer/Observer 被統(tǒng)稱(chēng)為?mode item,一個(gè) item 可以被同時(shí)加入多個(gè) mode垫毙。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會(huì)有效果的霹疫。如果一個(gè) mode 中一個(gè) item 都沒(méi)有,則 RunLoop 會(huì)直接退出综芥,不進(jìn)入循環(huán)丽蝎。
RunLoop 的內(nèi)部邏輯
根據(jù)蘋(píng)果在文檔里的說(shuō)明,RunLoop 內(nèi)部的邏輯大致如下:
官方文檔:
The Run Loop Sequence of Events
Each time you run it, your thread’s run loop processes pending events and generates notifications for any attached observers. The order in which it does this is very specific and is as follows:
Notify observers that the run loop has been entered.
Notify observers that any ready timers are about to fire.
Notify observers that any input sources that are not port based are about to fire.
Fire any non-port-based input sources that are ready to fire.
If a port-based input source is ready and waiting to fire, process the event immediately. Go to step 9.
Notify observers that the thread is about to sleep.
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.
Notify observers that the thread just woke up.
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.
Notify observers that the run loop has exited.
/// 用DefaultMode啟動(dòng)
'''
void CFRunLoopRun(void) {
????CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
}
'''
/// 用指定的Mode啟動(dòng)膀藐,允許設(shè)置RunLoop超時(shí)時(shí)間
'''
int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {
????return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
'''
以下是官方源碼 并沒(méi)有刪減代碼,這樣大家更好理解點(diǎn)
'''
SInt32CFRunLoopRunSpecific(CFRunLoopRefrl,CFStringRefmodeName,CFTimeIntervalseconds,BooleanreturnAfterSourceHandled) {? ? /* DOES CALLOUT */
? ? CHECK_FOR_FORK();
?? ? //檢查 run loop 是否正在銷(xiāo)毀
? ? if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
? ? __CFRunLoopLock(rl);
? ? // 查找 modeName 指定的 mode
? ? CFRunLoopModeRefcurrentMode =__CFRunLoopFindMode(rl, modeName,false);
? ? if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {// 沒(méi)有找到 mode 或者 mode 里面沒(méi)有任何事件源的話(huà)屠阻,返回 kCFRunLoopRunFinished
? ? ? ? //這里比較奇怪的是 Boolean did = false 直接寫(xiě)死了 did 的值,后面又是 return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished . 懷疑 did 的值额各,應(yīng)該還有一段代碼是決定kCFRunLoopRunHandledSource的結(jié)果国觉,被蘋(píng)果隱藏了沒(méi)有開(kāi)源出來(lái)
Booleandid =false;
if(currentMode)__CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
? ? }
? ? // 因?yàn)榭梢郧短渍{(diào)用,保存一下之前的狀態(tài)
? ? volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
? ? CFRunLoopModeRef previousMode = rl->_currentMode;
? ? rl->_currentMode= currentMode;
? ? int32_t result = kCFRunLoopRunFinished;
if(currentMode->_observerMask&kCFRunLoopEntry)__CFRunLoopDoObservers(rl, currentMode,kCFRunLoopEntry);
result =__CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
if(currentMode->_observerMask&kCFRunLoopExit)__CFRunLoopDoObservers(rl, currentMode,kCFRunLoopExit);
? ? ? ? __CFRunLoopModeUnlock(currentMode);
? ? ? ? __CFRunLoopPopPerRunData(rl, previousPerRun);
rl->_currentMode= previousMode;
? ? __CFRunLoopUnlock(rl);
? ? returnresult;
}
/**
?*? 運(yùn)行run loop
?*
?*? @param rl? ? ? ? ? ? ? 運(yùn)行的RunLoop對(duì)象
?*? @param rlm? ? ? ? ? ? 運(yùn)行的mode
?*? @param seconds? ? ? ? run loop超時(shí)時(shí)間
?*? @param stopAfterHandle true:run loop處理完事件就退出? false:一直運(yùn)行直到超時(shí)或者被手動(dòng)終止
?*? @param previousMode? ? 上一次運(yùn)行的mode
?*
?*? @return 返回4種狀態(tài)
?*/
/* rl, rlm are locked on entrance and exit */
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
? ? //mach_absolute_time is a CPU/Bus dependent function that returns a value based on the number of "ticks" since the system started up.
? ? // mach_absolute_time? 是一個(gè)CPU/總線依賴(lài)函數(shù)虾啦,返回一個(gè)基于系統(tǒng)啟動(dòng)后的時(shí)鐘"嘀嗒"數(shù)麻诀。在macOS上可以確保它的行為,并且缸逃,它包含系統(tǒng)時(shí)鐘所包含的所有時(shí)間區(qū)域针饥。其可獲取納秒級(jí)的精度
? ? //獲取系統(tǒng)啟動(dòng)之后cpu嘀嗒數(shù)
? ? uint64_tstartTSR =mach_absolute_time();
? ? if (__CFRunLoopIsStopped(rl)) {
? ? ? ? __CFRunLoopUnsetStopped(rl);
? ? ? ? return kCFRunLoopRunStopped;
? ? }elseif(rlm->_stopped) {
? ? ? ? rlm->_stopped =false;
? ? ? ? return kCFRunLoopRunStopped;
? ? }
? ? /*
?? ? kern_return_t mach_port_names?
?? ? (ipc_space_t? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? task,
?? ? mach_port_name_array_t? ? ? ? ? ? ? ? ? * names需频,
?? ? mach_msg_type_number_t? ? ? ? ? ? ? ? * namesCnt,
?? ? mach_port_type_array_? ? ? ? ? ? ? ? ? ? * types筷凤,
?? ? mach_msg_type_number_t? ? ? ? ? ? ? ? * typesCnt );
?? ? mach_port_name_t must be an unsigned type.? Port values
?? ? *? have two parts, a generation number and an index.
?? ? *? These macros encapsulate all knowledge of how
?? ? *? a mach_port_name_t is laid out.? They are made visible?
?? ? *? to user tasks so that packages to map from a mach_port_name_t
?? ? *? to associated user data can discount the generation
?? ? *? nuber (if desired) in doing the mapping.
?? ? *
?? ? *? Within the kernel, ipc/ipc_entry.c implicitly assumes
?? ? *? when it uses the splay tree functions that the generation
?? ? *? number is in the low bits, so that names are ordered first
?? ? *? by index and then by generation.? If the size of generation
?? ? *? numbers changes, be sure to update IE_BITS_GEN_MASK and
?? ? *? friends in ipc/ipc_entry.h.
?? ? 返回有關(guān)任務(wù)端口名稱(chēng)空間的信息昭殉。
?? ? 該mach_port_names返回有關(guān)任務(wù)的端口名稱(chēng)空間。它返回任務(wù)的當(dāng)前活動(dòng)名稱(chēng)藐守,表示某些端口挪丢,端口集或死命名。對(duì)于每個(gè)名稱(chēng)卢厂,它還返回任何類(lèi)型的權(quán)限 任務(wù)(由mach_port_type返回的相同信息)乾蓬。
?? ? 請(qǐng)注意,當(dāng)對(duì)mach_port_names的調(diào)用返回時(shí)慎恒,兩個(gè)輸出數(shù)組(名稱(chēng)和類(lèi)型)中的條目數(shù)相等(namesCnt等于typesCnt)任内。此接口返回兩個(gè)單獨(dú)計(jì)數(shù)的事實(shí)是Mach Interface Generator的工件撵渡。
?? ? task
?? ? 查詢(xún)端口名稱(chēng)空間的任務(wù)。
?? ? names
?? ? [指向動(dòng)態(tài)數(shù)組mach_port_name_t的指針]任務(wù)端口名稱(chēng)空間中的端口死嗦,端口集和死名稱(chēng)的名稱(chēng)趋距,沒(méi)有特定的順序。
?? ? namesCnt
?? ? [out scalar]返回的名稱(chēng)數(shù)量越除。
?? ? types
?? ? [指向動(dòng)態(tài)數(shù)組mach_port_type_t的指針]每個(gè)相應(yīng)名稱(chēng)的類(lèi)型节腐。指示任務(wù)使用該名稱(chēng)保留的權(quán)限類(lèi)型。
?? ? typesCnt
?? ? [out scalar]返回的類(lèi)型數(shù)摘盆。
?? ? 返回值
?? ? 僅適用一般性錯(cuò)誤翼雀。
?? ? */
? ? //mach 端口, 線程之間通信的對(duì)象? mach端口,內(nèi)核進(jìn)程通信消息端口孩擂。初始為0
? ? mach_port_name_tdispatchPort =MACH_PORT_NULL;
? ? //pthread_main_np() 獲取主線程 這里主要是為了判斷當(dāng)前線程是否為主線程?
? ? // 檢測(cè)是否在主線程 && ( (是隊(duì)列發(fā)的消息&&mode為null)||(不是隊(duì)列發(fā)的消息&&不在主隊(duì)列))
? ? BooleanlibdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY &&NULL== previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY &&0== _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
? ? /*
?? ? 需要在主線程锅纺,run loop 也是主線程的 run loop,并且 mode 是 common mode
?? ? 從 GCD 的私有 API 獲取端口(4CF 表示 for Core Foundation)
?? ? 則給 dispatchPort 賦值為主線程收發(fā)消息的端口
?? ? */
? ? //如果是隊(duì)列安全的肋殴,并且是主線程runloop,設(shè)置它對(duì)應(yīng)的通信端口
? ? /*
?? ? _dispatch_get_main_queue_port_4CF是 dispatch_get_main_queue_handle_4CF 的宏囤锉,存在 libdispatch 中,里面對(duì)它的實(shí)現(xiàn)為:
?? ? dispatch_runloop_handle_t
?? ? _dispatch_get_main_queue_handle_4CF(void)
?? ? {
?? ? dispatch_queue_t dq = &_dispatch_main_q;
?? ? dispatch_once_f(&_dispatch_main_q_handle_pred, dq,
?? ? _dispatch_runloop_queue_handle_init);
?? ? return _dispatch_runloop_queue_get_handle(dq);
?? ? }
?? ? 返回的是主線程 runloop 所關(guān)聯(lián)的的端口护锤。
?? ? */
? ? if(libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort =_dispatch_get_main_queue_port_4CF();
? ? /*
?? ? USE_DISPATCH_SOURCE_FOR_TIMERS 這個(gè)宏的值為 1官地,也就是說(shuō)有使用 GCD 來(lái)實(shí)現(xiàn) timer,當(dāng)然 USE_MK_TIMER_TOO 這個(gè)宏的值也是 1烙懦,表示也使用了更底層的 timer驱入。
?? ? */
? ? //如果使用 GCD timer 作為 timer 的實(shí)現(xiàn)的話(huà),進(jìn)行準(zhǔn)備工作
#if USE_DISPATCH_SOURCE_FOR_TIMERS
? ? //? MACOSX 下氯析,聲明一個(gè) mode 的隊(duì)列通信端口(在 MACOSX 環(huán)境中):
? ? mach_port_name_t modeQueuePort = MACH_PORT_NULL;
? ? if(rlm->_queue) {
? ? ? ? modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
? ? ? ? if(!modeQueuePort) {
? ? ? ? ? ? CRASH("Unable to get port for run loop mode queue (%d)", -1);
? ? ? ? }
? ? }
#endif
? ? //使用GCD實(shí)現(xiàn)runloop超時(shí)功能?
? ? //GCD管理的定時(shí)器活箕,用于實(shí)現(xiàn)runloop超時(shí)機(jī)制
? ? dispatch_source_ttimeout_timer =NULL;
? ? //創(chuàng)建上下文 跟GCD 定時(shí)器關(guān)聯(lián)一起
? ? struct__timeout_context *timeout_context = (struct__timeout_context *)malloc(sizeof(*timeout_context));
? ? if (seconds <= 0.0) { // instant timeout//立即超時(shí)
? ? ? ? seconds =0.0;
? ? ? ? timeout_context->termTSR =0ULL;
? ? }elseif(seconds <= TIMER_INTERVAL_LIMIT) {// seconds為超時(shí)時(shí)間,超時(shí)時(shí)執(zhí)行__CFRunLoopTimeout函數(shù)?
? ? ? ? //根據(jù)是否為主線程讯壶,設(shè)置隊(duì)列是主隊(duì)列還是后臺(tái)隊(duì)列
? ? ? ? dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
? ? ? ? timeout_timer =dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0,0, queue);
? ? ? ? dispatch_retain(timeout_timer);
? ? ? ? timeout_context->ds = timeout_timer;
? ? ? ? //runloop
? ? ? ? timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
? ? ? ? timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
? ? ? ? /*
?? ? ? ? void dispatch_set_context(dispatch_object_t object, void *context);
?? ? ? ? 將應(yīng)用程序定義的上下文與對(duì)象相關(guān)聯(lián)鼠渺。
?? ? ? ? 您的應(yīng)用程序可以將自定義上下文數(shù)據(jù)與對(duì)象相關(guān)聯(lián),僅供您的應(yīng)用程序使用你辣。您的應(yīng)用程序必須根據(jù)需要分配和取消分配數(shù)據(jù)巡通。
?? ? ? ? object
?? ? ? ? 這個(gè)參數(shù)不能NULL。
?? ? ? ? context
?? ? ? ? 對(duì)象的新應(yīng)用程序定義的上下文舍哄。這可以NULL宴凉。
?? ? ? ? void dispatch_source_set_event_handler_f(dispatch_source_t source, dispatch_function_t handler);
?? ? ? ? 為給定的調(diào)度源設(shè)置事件處理函數(shù)。
?? ? ? ? source
?? ? ? ? 調(diào)度源要修改表悬。這個(gè)參數(shù)不能NULL弥锄。
?? ? ? ? handler
?? ? ? ? 事件處理函數(shù)提交到源的目標(biāo)隊(duì)列。傳遞給事件處理函數(shù)的context參數(shù)是處理程序調(diào)用時(shí)調(diào)度源的當(dāng)前上下文。這個(gè)參數(shù)不能NULL籽暇。
?? ? ? ? */
? ? ? ? dispatch_set_context(timeout_timer, timeout_context);// source gets ownership of context
? ? ? ? dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
? ? ? ? dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
? ? ? ? uint64_tns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) *1000000000ULL);
? ? ? ? dispatch_source_set_timer(timeout_timer,dispatch_time(1, ns_at),DISPATCH_TIME_FOREVER,1000ULL);
? ? ? ? //恢復(fù)喚起 timer 執(zhí)行
? ? ? ? dispatch_resume(timeout_timer);
? ? }else { // infinite timeout // 永不超時(shí)
? ? ? ? //無(wú)限期超時(shí)
? ? ? ? seconds =9999999999.0;
? ? ? ? timeout_context->termTSR =UINT64_MAX;
? ? }
? ? //設(shè)置超時(shí)timer 結(jié)束? 標(biāo)志位默認(rèn)為true
? ? // 設(shè)置判斷是否為最后一次 dispatch 的端口通信的變量
? ? BooleandidDispatchPortLastTime =true;
? ? //記錄最后runloop狀態(tài)温治,用于return 退出 runloop
? ? // 設(shè)置一個(gè)結(jié)果變量,最后為幾個(gè) CFRunLoopRunInMode 里返回狀態(tài)之一图仓。
? ? int32_tretVal =0;
? ? do{
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
? ? ? ? // 一個(gè)狀態(tài)變量罐盔,用于 消息狀態(tài) 標(biāo)志,初始值為 UNCHAMGED
? ? ? ? voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
? ? ? ? voucher_t voucherCopy =NULL;
#endif
? ? ? ? // 初始化一個(gè)存放內(nèi)核消息的緩沖池
? ? ? ? uint8_t msg_buffer[3*1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
? ? ? ? //聲明和 mach port 有關(guān)的 port 和 msg 變量
? ? ? ? mach_msg_header_t *msg =NULL;
? ? ? ? // 活動(dòng)端口救崔,本地用來(lái)接收消息的端口
? ? ? ? mach_port_t livePort = MACH_PORT_NULL;
#endif
? ? ? ? // 取所有需要監(jiān)聽(tīng)的port,runloopMode
? ? ? ? // 聲明一個(gè)類(lèi)型為 CFPortSet 的 waitSet, 值為 run loop mode 里的 portSet.
? ? ? ? __CFPortSet waitSet = rlm->_portSet;
? ? ? ? // 設(shè)置RunLoop為可以被喚醒狀態(tài)
? ? ? ? //將 run loop 從忽略喚醒消息的狀態(tài) unset ,開(kāi)始接受喚醒消息
? ? ? ? __CFRunLoopUnsetIgnoreWakeUps(rl);
? ? ? ? //2. 通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)
? ? ? ? if(rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
? ? ? ? //3. 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)惶看。
? ? ? ? if(rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
? ? ? ? // 執(zhí)行被加入的block
? ? ? ? __CFRunLoopDoBlocks(rl, rlm);
? ? ? ? //4. RunLoop 觸發(fā) Source0 (非port) 回調(diào)。
? ? ? ? // 執(zhí)行 Source0 (非 mach port) 六孵。 有事件處理返回 true纬黎,沒(méi)有事件返回 false
? ? ? ? Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
? ? ? ? if(sourceHandledThisLoop) {
? ? ? ? ? ? // 執(zhí)行加入當(dāng)前 runloop 的 block
? ? ? ? ? ? __CFRunLoopDoBlocks(rl, rlm);
? ? ? ? }
? ? ? ? // 如果沒(méi)有 Sources0 事件處理 并且 沒(méi)有超時(shí),poll 為 false
? ? ? ? // 如果有 Sources0 事件處理 或者 超時(shí)劫窒,poll 都為 true
? ? ? ? Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
? ? ? ? //5. 如果有 Source1 (基于port) 處于 ready 狀態(tài)本今,直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息
? ? ? ? // 第一次do..whil循環(huán)不會(huì)走該分支,因?yàn)?didDispatchPortLastTime 初始化是 true
? ? ? ? if(MACH_PORT_NULL!= dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
? ? ? ? ? ? // 從緩沖區(qū)讀取消息
? ? ? ? ? ? msg = (mach_msg_header_t *)msg_buffer;
? ? ? ? ? ? // 5. 接收 dispatchPort 端口的消息主巍,dispatch 到 main queue 的事件冠息。
? ? ? ? ? ? if(__CFRunLoopServiceMachPort(dispatchPort, &msg,sizeof(msg_buffer), &livePort,0, &voucherState,NULL)) {
? ? ? ? ? ? ? ? // 如果接收到了消息的話(huà),前往 handle_msg 開(kāi)始處理 msg
? ? ? ? ? ? ? ? gotohandle_msg;
? ? ? ? ? ? }
#endif
? ? ? ? }
? ? ? ? didDispatchPortLastTime =false;
? ? ? ? //6, 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)
? ? ? ? // 注意到如果實(shí)際處理了 source0 或者超時(shí)孕索,不會(huì)進(jìn)入睡眠逛艰,所以不會(huì)通知。
? ? ? ? if(!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
? ? ? ? //設(shè)置標(biāo)志位搞旭,正在睡眠(實(shí)際上沒(méi)有開(kāi)始睡)
? ? ? ? // 設(shè)置RunLoop為休眠狀態(tài)
? ? ? ? // 設(shè)置標(biāo)志位散怖, Run Loop 休眠
? ? ? ? __CFRunLoopSetSleeping(rl);
? ? ? ? // do not do any user callouts after this point (after notifying of sleeping)
? ? ? ? // Must push the local-to-this-activation ports in on every loop
? ? ? ? // iteration, as this mode could be run re-entrantly and we don't
? ? ? ? // want these ports to get serviced.
? ? ? ? //使用 GCD 的話(huà),將 GCD 端口加入所有監(jiān)聽(tīng)端口集合中
? ? ? ? __CFPortSetInsert(dispatchPort, waitSet);
? ? ? ? __CFRunLoopModeUnlock(rlm);
? ? ? ? __CFRunLoopUnlock(rl);
? ? ? ? //使用 GCD 的話(huà)肄渗,將 GCD 端口加入所有監(jiān)聽(tīng)端口集合中
? ? ? ? // 休眠開(kāi)始的時(shí)間镇眷,根據(jù) poll 狀態(tài)決定為 0 或者當(dāng)前的絕對(duì)時(shí)間
? ? ? ? CFAbsoluteTime sleepStart = poll ?0.0: CFAbsoluteTimeGetCurrent();
? ? ? ? // 這里有個(gè)內(nèi)循環(huán),用于接收等待端口的消息
? ? ? ? // 進(jìn)入此循環(huán)后翎嫡,線程進(jìn)入休眠欠动,直到收到新消息才跳出該循環(huán),繼續(xù)執(zhí)行run loop
? ? ? ? //// 等待被喚醒钝的,可以被 sources 1翁垂、timers、CFRunLoopWakeUp 函數(shù)和 GCD 事件(如果在主線程)
? ? ? ? //7.通過(guò) CFRunLoopServiceMachPort 調(diào)用 mach_msg 休眠,等待被 mach_msg 消息喚醒
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
? ? ? ? // 如果在 MACOSX 中
#if USE_DISPATCH_SOURCE_FOR_TIMERS
? ? ? ? // 處理 GCD timer?
? ? ? ? do{
? ? ? ? ? ? // 使用 GCD timer 作為 timer 實(shí)現(xiàn)的情況
? ? ? ? ? ? if (kCFUseCollectableAllocator) {//假如有kCFUseCollectableAllocator分配器硝桩,使用 memset 清空msg_buffer
? ? ? ? ? ? ? ? // objc_clear_stack(0);
? ? ? ? ? ? ? ? // <rdar://problem/16393959>
? ? ? ? ? ? ? ? // 清空 msg_buffer
? ? ? ? ? ? ? ? memset(msg_buffer,0,sizeof(msg_buffer));
? ? ? ? ? ? }
? ? ? ? ? ? msg = (mach_msg_header_t *)msg_buffer;
? ? ? ? ? ? // 這個(gè)函數(shù)會(huì)睡眠線程 waitSet: 監(jiān)聽(tīng)端口集合? livePort: 返回收到消息的端口 poll: 根據(jù)狀態(tài)睡眠或者不睡
? ? ? ? ? ? // 接收waitSet端口的消息
? ? ? ? ? ? // 設(shè)置 mach port 通信,會(huì)睡眠線程
? ? ? ? ? ? __CFRunLoopServiceMachPort(waitSet, &msg,sizeof(msg_buffer), &livePort, poll ?0: TIMEOUT_INFINITY, &voucherState, &voucherCopy);
? ? ? ? ? ? // 如果是 timer 端口喚醒的枚荣,進(jìn)行一下善后處理碗脊,之后再處理 timer
? ? ? ? ? ? // 收到消息之后,livePort 的值為本地接收消息的活動(dòng)端口
? ? ? ? ? ? // modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
? ? ? ? ? ? //modeQueuePort:如果使用 GCD timer 作為 timer 的實(shí)現(xiàn)的話(huà),進(jìn)行準(zhǔn)備工作
? ? ? ? ? ? //livePort 作為本地接受端口
? ? ? ? ? ? // modeQueue 存在衙伶,而且為 livePort
? ? ? ? ? ? if(modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
? ? ? ? ? ? ? ? // Drain the internal queue. If one of the callout blocks sets the timerFired flag, break out and service the timer.
? ? ? ? ? ? ? ? //執(zhí)行 run loop mode 里的隊(duì)列祈坠,直到隊(duì)列都執(zhí)行完成
? ? ? ? ? ? ? ? while(_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
? ? ? ? ? ? ? ? if (rlm->_timerFired) {//timer 喚醒的//假如 _timerFired 為真,把 livePort 作為隊(duì)列端口矢劲,在之前服務(wù)于 timers
? ? ? ? ? ? ? ? ? ? // Leave livePort as the queue port, and service timers below
? ? ? ? ? ? ? ? ? ? rlm->_timerFired =false;
? ? ? ? ? ? ? ? ? ? break;
? ? ? ? ? ? ? ? }else {// _timerFired 為假, 并且 msg 存在不為 msg_buffer, 釋放 msg
? ? ? ? ? ? ? ? ? ? if(msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }else{
? ? ? ? ? ? ? ? // Go ahead and leave the inner loop.
? ? ? ? ? ? ? ? // 不是 timer 端口喚醒的赦拘,進(jìn)行接下來(lái)的處理
? ? ? ? ? ? ? ? break;
? ? ? ? ? ? }
? ? ? ? }while(1);
#else// 不在 MACOSX 中
? ? ? ? // 不使用 GCD timer 作為 timer 實(shí)現(xiàn)的情況
? ? ? ? if (kCFUseCollectableAllocator) {//如果 kCFUseCollectableAllocator 分配器,使用 memset 清空 msg_buffer
? ? ? ? ? ? // objc_clear_stack(0);
? ? ? ? ? ? // <rdar://problem/16393959>
? ? ? ? ? ? memset(msg_buffer,0,sizeof(msg_buffer));
? ? ? ? }
? ? ? ? /// . 調(diào)用 mach_msg 等待接受 mach_port 的消息芬沉。線程將進(jìn)入休眠, 直到被下面某一個(gè)事件喚醒躺同。
? ? ? ? /// ? 一個(gè)基于 port 的Source 的事件。
? ? ? ? /// ? 一個(gè) Timer 到時(shí)間了
? ? ? ? /// ? RunLoop 自身的超時(shí)時(shí)間到了
? ? ? ? /// ? 被其他什么調(diào)用者手動(dòng)喚醒
? ? ? ? msg = (mach_msg_header_t *)msg_buffer;
? ? ? ? //CFRunLoopServiceMachPort 會(huì)讓線程休眠
? ? ? ? __CFRunLoopServiceMachPort(waitSet, &msg,sizeof(msg_buffer), &livePort, poll ?0: TIMEOUT_INFINITY, &voucherState, &voucherCopy);
#endif
? ? ? ? //上鎖
? ? ? ? __CFRunLoopLock(rl);
? ? ? ? __CFRunLoopModeLock(rlm);
? ? ? ? // 增加記錄的睡眠時(shí)間
? ? ? ? // 根據(jù) poll 的值丸逸,記錄休眠時(shí)間,休眠時(shí)間差
? ? ? ? rl->_sleepTime += (poll ?0.0: (CFAbsoluteTimeGetCurrent() - sleepStart));
? ? ? ? // Must remove the local-to-this-activation ports in on every loop
? ? ? ? // iteration, as this mode could be run re-entrantly and we don't
? ? ? ? // want these ports to get serviced. Also, we don't want them left
? ? ? ? // in there if this function returns.
? ? ? ? // 將 GCD 端口移除
? ? ? ? //對(duì) waitSet 里的 dispatchPort 端口做移除
? ? ? ? __CFPortSetRemove(dispatchPort, waitSet);
? ? ? ? // 設(shè)置 runloop 不可被喚醒
? ? ? ? //讓 Run Loop 忽略喚醒消息蹋艺,因?yàn)橐呀?jīng)重新在運(yùn)行了
? ? ? ? __CFRunLoopSetIgnoreWakeUps(rl);
? ? ? ? //? 取消runloop的休眠狀態(tài)
? ? ? ? // user callouts now OK again
? ? ? ? __CFRunLoopUnsetSleeping(rl);
? ? ? ? // 8 通知 observers: kCFRunLoopAfterWaiting, 即停止等待(被喚醒)
? ? ? ? // 注意實(shí)際處理過(guò) source 0 或者已經(jīng)超時(shí)的話(huà),不會(huì)通知(因?yàn)闆](méi)有睡)
? ? ? ? if(!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
? ? ? ? // 11. 被什么喚醒就處理什么: 處理通過(guò)端口收到的消息
? ? handle_msg:;
? ? ? ? // 設(shè)置 runloop 不可被喚醒
? ? ? ? //將 Run Loop 重新忽略喚醒消息黄刚,因?yàn)橐呀?jīng)重新在運(yùn)行了
? ? ? ? __CFRunLoopSetIgnoreWakeUps(rl);
? ? ? ? if (MACH_PORT_NULL == livePort) {// 不知道哪個(gè)端口喚醒的(或者根本沒(méi)睡)捎谨,啥也不干? livePort 為空,什么事都不做
? ? ? ? ? ? CFRUNLOOP_WAKEUP_FOR_NOTHING();
? ? ? ? ? ? // handle nothing
? ? ? ? }else if (livePort == rl->_wakeUpPort) {// 被 CFRunLoopWakeUp 函數(shù)弄醒的憔维,啥也不干 跳回2重新循環(huán) // livePort 等于 run loop 的 _wakeUpPort
? ? ? ? ? ? // 被 CFRunLoopWakeUp 函數(shù)喚醒的
? ? ? ? ? ? CFRUNLOOP_WAKEUP_FOR_WAKEUP();
? ? ? ? ? ? // do nothing on Mac OS
? ? ? ? }
#if USE_DISPATCH_SOURCE_FOR_TIMERS// 在 MACOSX 里
? ? ? ? elseif(modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {//被 timers 喚醒涛救,處理 timers?
? ? ? ? ? ? //livePort 等于 modeQueuePort
? ? ? ? ? ? //9.1-1 被 timers 喚醒,處理 timers
? ? ? ? ? ? CFRUNLOOP_WAKEUP_FOR_TIMER();
? ? ? ? ? ? if(!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
? ? ? ? ? ? ? ? // Re-arm the next timer, because we apparently fired early
? ? ? ? ? ? ? ? __CFArmNextTimerInMode(rlm, rl);
? ? ? ? ? ? }
? ? ? ? }
#endif
#if USE_MK_TIMER_TOO
? ? ? ? elseif(rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {// 被 模mk timers 喚醒业扒,處理 timers
? ? ? ? ? ? //livePort 等于 run loop mode 的 _timerPort
? ? ? ? ? ? // 9.1-2 被 timers 喚醒检吆,處理 timers
? ? ? ? ? ? CFRUNLOOP_WAKEUP_FOR_TIMER();
? ? ? ? ? ? // 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.
? ? ? ? ? ? // 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
? ? ? ? ? ? if(!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
? ? ? ? ? ? ? ? // Re-arm the next timer
? ? ? ? ? ? ? ? __CFArmNextTimerInMode(rlm, rl);
? ? ? ? ? ? }
? ? ? ? }
#endif
? ? ? ? else if (livePort == dispatchPort) {//9.2 如果有dispatch到main_queue的block,執(zhí)行block凶赁。
? ? ? ? ? ? // 被 GCD 喚醒或者從第 7 步跳轉(zhuǎn)過(guò)來(lái)的話(huà)咧栗,處理 GCD
? ? ? ? ? ? CFRUNLOOP_WAKEUP_FOR_DISPATCH();
? ? ? ? ? ? __CFRunLoopModeUnlock(rlm);
? ? ? ? ? ? __CFRunLoopUnlock(rl);
? ? ? ? ? ? //設(shè)置 CFTSDKeyIsInGCDMainQ 位置的 TSD 為 6 .
? ? ? ? ? ? _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void*)6,NULL);
? ? ? ? ? ? // 執(zhí)行block
? ? ? ? ? ? // 處理 msg
? ? ? ? ? ? __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
? ? ? ? ? ? //設(shè)置 CFTSDKeyIsInGCDMainQ 位置的 TSD 為 0.
? ? ? ? ? ? _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void*)0,NULL);
? ? ? ? ? ? __CFRunLoopLock(rl);
? ? ? ? ? ? __CFRunLoopModeLock(rlm);
? ? ? ? ? ? //設(shè)置變量
? ? ? ? ? ? sourceHandledThisLoop =true;
? ? ? ? ? ? didDispatchPortLastTime =true;
? ? ? ? }else{
? ? ? ? ? ? // source1 事件?
? ? ? ? ? ? //被 source (基于 mach port) 喚醒
? ? ? ? ? ? CFRUNLOOP_WAKEUP_FOR_SOURCE();
? ? ? ? ? ? // If we received a voucher from this mach_msg, then put a copy of the new voucher into TSD. CFMachPortBoost will look in the TSD for the voucher. By using the value in the TSD we tie the CFMachPortBoost to this received mach_msg explicitly without a chance for anything in between the two pieces of code to set the voucher again.
? ? ? ? ? ? // 假如我們 從這個(gè) mach_msg 中接收到一個(gè) voucher,然后在 TSD 中放置一個(gè)復(fù)制的新的 voucher.
? ? ? ? ? ? // CFMachPortBoost 會(huì)在 TSD 中去查找這個(gè) voucher.?
? ? ? ? ? ? // 通過(guò)使用 TSD 中的值虱肄,我們將 CFMachPortBoost 綁定到這個(gè)接收到的 mach_msg 中致板,在這兩段代碼之間沒(méi)有任何機(jī)會(huì)再次設(shè)置憑證
? ? ? ? ? ? voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void*)voucherCopy, os_release);
? ? ? ? ? ? // 被 sources 1 喚醒,處理 sources 1
? ? ? ? ? ? //9.3 如果一個(gè) Source1 (基于port) 發(fā)出事件了咏窿,處理這個(gè)事件
? ? ? ? ? ? // Despite the name, this works for windows handles as well
? ? ? ? ? ? // 根據(jù)接收消息的 port 尋找 source1 事件
? ? ? ? ? ? CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
? ? ? ? ? ? if (rls) {// 有 source1 事件待處理? //如果 rls 存在
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
? ? ? ? ? ? ? ? mach_msg_header_t *reply =NULL;
? ? ? ? ? ? ? ? // 處理 source1 事件? //處理 Source ,并返回執(zhí)行結(jié)果
? ? ? ? ? ? ? ? sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
? ? ? ? ? ? ? ? if(NULL!= reply) {//發(fā)送reply消息(假如 reply 不為空)
? ? ? ? ? ? ? ? ? ? (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size,0, MACH_PORT_NULL,0, MACH_PORT_NULL);
? ? ? ? ? ? ? ? ? ? //釋放 reply 變量
? ? ? ? ? ? ? ? ? ? CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
? ? ? ? ? ? ? ? }
#endif
? ? ? ? ? ? }
? ? ? ? ? ? // Restore the previous voucher
? ? ? ? ? ? _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, previousVoucher, os_release);
? ? ? ? }?
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
? ? ? ? if(msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif
? ? ? ? //12. 再一次處理 blocks
? ? ? ? __CFRunLoopDoBlocks(rl, rlm);
? ? ? ? // 13. 判斷是否退出斟或,不需要退出則跳轉(zhuǎn)回第 2 步? //根據(jù)一次循環(huán)后的狀態(tài),給 retVal 賦值 集嵌。狀態(tài)不變則繼續(xù)循環(huán)
? ? ? ? if(sourceHandledThisLoop && stopAfterHandle) {
? ? ? ? ? ? retVal = kCFRunLoopRunHandledSource;
? ? ? ? }elseif(timeout_context->termTSR < mach_absolute_time()) {
? ? ? ? ? ? retVal = kCFRunLoopRunTimedOut;
? ? ? ? }elseif(__CFRunLoopIsStopped(rl)) {
? ? ? ? ? ? __CFRunLoopUnsetStopped(rl);
? ? ? ? ? ? retVal = kCFRunLoopRunStopped;
? ? ? ? }elseif(rlm->_stopped) {
? ? ? ? ? ? rlm->_stopped =false;
? ? ? ? ? ? retVal = kCFRunLoopRunStopped;
? ? ? ? }elseif(__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
? ? ? ? ? ? retVal = kCFRunLoopRunFinished;
? ? ? ? }
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
? ? ? ? // 循環(huán)一次后收尾處理
? ? ? ? voucher_mach_msg_revert(voucherState);
? ? ? ? os_release(voucherCopy);
#endif
? ? }while(0== retVal);
? ? if(timeout_timer) {//如果存在萝挤,取消并釋放
? ? ? ? dispatch_source_cancel(timeout_timer);
? ? ? ? dispatch_release(timeout_timer);
? ? }else {//不存在,將對(duì)應(yīng)的 timeour_context 釋放
? ? ? ? free(timeout_context);
? ? }
? ? //結(jié)束返回 retVal 狀態(tài)根欧。
? ? return retVal;
}
'''
可以看到怜珍,實(shí)際上 RunLoop 就是這樣一個(gè)函數(shù),其內(nèi)部是一個(gè) do-while 循環(huán)凤粗。當(dāng)你調(diào)用 CFRunLoopRun() 時(shí)酥泛,線程就會(huì)一直停留在這個(gè)循環(huán)里;直到超時(shí)或被手動(dòng)停止,該函數(shù)才會(huì)返回柔袁。