RunLoop 運(yùn)行機(jī)制原理邏輯與GCD及線程關(guān)系剖析

前言

文章主要會RunLoop源碼進(jìn)行剖析,里面會有對它的理解及注釋,有不足望見解

1,RunLoop是什么?

廣義上的來說,run loop 就是所謂的 event loop,或者稱之為「事件循環(huán)」或者「事件分發(fā)器」。Event loop 是 event-driven programming(事件驅(qū)動編程)非常重要的組成部分孩灯,而事件驅(qū)動編程則是 GUI 程序的最常見編程方式(現(xiàn)在似乎在服務(wù)器端也有很多應(yīng)用,但在 GUI 編程方面肯定是繞不過去)逾滥。

Event Loop

Run Loop 是一個(gè) iOS 開發(fā)里的基礎(chǔ)概念峰档,它并非獨(dú)有的機(jī)制,很多系統(tǒng)和框架都有類似的實(shí)現(xiàn)寨昙,Run Loop 是 Event Loop (事件循環(huán))機(jī)制的在 iOS 平臺的一種實(shí)現(xiàn)讥巡。

Event loop 的思想非常簡單,用下面的偽代碼來表示:

int main(void){

    初始化();

    while(message !=退出){

        處理事件(message);

        message =獲取下一個(gè)事件();

    }

    return 0;

}

蘋果官網(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í)保持線程忙捉蚤,并在沒有工作時(shí)讓線程進(jìn)入休眠狀態(tài)抬驴。

運(yùn)行循環(huán)管理不是完全自動的。您仍然必須設(shè)計(jì)線程的代碼以在適當(dāng)?shù)臅r(shí)間啟動運(yùn)行循環(huán)并響應(yīng)傳入的事件缆巧。Cocoa和Core Foundation都提供了運(yùn)行循環(huán)對象來幫助您配置和管理線程的運(yùn)行循環(huán)布持。您的應(yīng)用程序不需要顯式創(chuàng)建這些對象; 每個(gè)線程(包括應(yīng)用程序的主線程)都有一個(gè)關(guān)聯(lián)的運(yùn)行循環(huán)對象。但是陕悬,只有輔助線程需要顯式運(yùn)行其運(yùn)行循環(huán)题暖。作為應(yīng)用程序啟動過程的一部分,應(yīng)用程序框架會自動在主線程上設(shè)置并運(yùn)行運(yùn)行循環(huán)捉超。

以下部分提供有關(guān)運(yùn)行循環(huán)以及如何為應(yīng)用程序配置它們的更多信息胧卤。有關(guān)運(yùn)行循環(huán)對象的其他信息,請參閱NSRunLoop類參考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)及其來源

image

回到 macOS/iOS 平臺上灌侣,對于 event loop 的具體實(shí)現(xiàn)有兩個(gè):

Foundation 框架中的 NSRunLoop

Core Foundation 框架中的 CFRunLoop

其中 NSRunLoop 是對 CFRunLoop 的簡單封裝,需要著重研究的只有 CFRunLoop裂问。

2,Run Loop 實(shí)現(xiàn)

網(wǎng)上目前有關(guān) Run Loop 的文章侧啼, 10 篇里面可能有 8 篇都是重復(fù)了 深入理解RunLoop 中的代碼牛柒。包括本人閱讀不下5遍,在閱讀runloop源碼前后都去閱讀YY作者文章,然而這都是經(jīng)過作者大量簡化過的版本,隱藏了大量的細(xì)節(jié)痊乾。

RunLoop 與線程的關(guān)系

首先皮壁,iOS 開發(fā)中能遇到兩個(gè)線程對象: pthread_t 和 NSThread。過去蘋果有份文檔標(biāo)明了 NSThread 只是 pthread_t 的封裝哪审,但那份文檔已經(jīng)失效了蛾魄,現(xiàn)在它們也有可能都是直接包裝自最底層的 mach thread。蘋果并沒有提供這兩個(gè)對象相互轉(zhuǎn)換的接口湿滓,但不管怎么樣滴须,可以肯定的是 pthread_t 和 NSThread 是一一對應(yīng)的。比如叽奥,你可以通過 pthread_main_thread_np() 或 [NSThread mainThread] 來獲取主線程扔水;也可以通過 pthread_self() 或 [NSThread currentThread] 來獲取當(dāng)前線程。CFRunLoop 是基于 pthread 來管理的朝氓。

蘋果不允許直接創(chuàng)建 RunLoop魔市,它只提供了兩個(gè)自動獲取的函數(shù):CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 這兩個(gè)函數(shù)內(nèi)部的邏輯大概是下面這樣:


CFRunLoopGetMain :

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;

}

CFRunLoopRef CFRunLoopGetCurrent(void) {

    CHECK_FOR_FORK();

    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);

    if(rl)returnrl;

    return _CFRunLoopGet0(pthread_self());

}

CFRunLoopGet0

無論是 CFRunLoopGetMain 還是 CFRunLoopGetCurrent 赵哲,兩者調(diào)用了 CFRunLoopGet0 :

// 全局的Dictionary待德,key 是 pthread_t, value 是 CFRunLoopRef

static CFMutableDictionaryRef   __CFRunLoops = NULL;

/// 訪問 loopsDic 時(shí)的鎖

static  CFLock_t  loopsLock = CFLockInit;

// should only be called by Foundation

// t==0 is a synonym for "main thread" that always works

/// 獲取一個(gè) pthread 對應(yīng)的 RunLoop枫夺。

CF_EXPORT  CFRunLoopRef  _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)) {

               // 注冊一個(gè)回調(diào)毙死,當(dāng)線程銷毀時(shí),順便也銷毀其對應(yīng)的 RunLoop喻鳄。

            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void*)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void(*)(void*))__CFFinalizeRunLoop);

        }

    }

    returnloop;

}

從上面的代碼可以看出扼倘,
1,線程和 RunLoop 之間是一一對應(yīng)的,其關(guān)系是保存在一個(gè)全局的 Dictionary 里,線程作為key,RunLoop作為value除呵。
2,線程剛創(chuàng)建時(shí)并沒有 RunLoop再菊,如果你不主動獲取,那它一直都不會有,主線程的RunLoop已經(jīng)自動創(chuàng)建好了,子線程的RunLoop需要主動創(chuàng)建颜曾。
3,RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí)纠拔,RunLoop 的銷毀是發(fā)生在線程結(jié)束時(shí)。
4,你只能在一個(gè)線程的內(nèi)部獲取其 RunLoop(主線程除外)泛豪。

CHECK_FOR_FORK()
在兩個(gè)函數(shù)里稠诲,都有使用了 CHECK_FOR_FORK() 侦鹏。
它應(yīng)該是屬于多進(jìn)程情況下的一個(gè)斷言。

Threading Programming Guide 中臀叙,有這么一段話:

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.

也就是說略水,當(dāng)通過 fork 啟動一個(gè)新進(jìn)程的時(shí)候,你必須要接著調(diào)用一個(gè) exec 或類似的函數(shù)劝萤。而依賴于 Core Founadtion / Cocoa / Core Data 框架的應(yīng)用,必須調(diào)用 exec 函數(shù),否則這些框架也許不能正確的工作渊涝。

所以為了保證安全,使用 CHECK_FOR_FORK 進(jìn)行檢查床嫌。

FORK

這里簡單提一下 fork 跨释。
在 UNIX 中,用 fork 來創(chuàng)建子進(jìn)程厌处,調(diào)用 fork( ) 的進(jìn)程被稱為父進(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不變,但原來進(jìn)程的代碼段函荣、堆棧段显押、數(shù)據(jù)段被新的內(nèi)容取代,來執(zhí)行新的程序傻挂。

這樣 fork 和 exec 就成為一種組合乘碑。

而在 iOS 這樣的類 UNIX 系統(tǒng)里,基本上也都要通過 fork 的形式來創(chuàng)建新的進(jìn)程金拒。

假如沒有執(zhí)行完 exec 兽肤,那么執(zhí)行的代碼段等內(nèi)容,還是父進(jìn)程里的绪抛,出現(xiàn)問題可以說百分之百资铡。這就是 CHECK_FOR_FORK 檢查的目的。

RunLoop結(jié)構(gòu)體

通過源碼我們找到__CFRunLoop結(jié)構(gòu)體

#pragma mark -
#pragma mark Run Loops

struct _block_item {
    struct _block_item *_next;
    CFTypeRef _mode;    // CFString or CFSet
    void (^_block)(void);
};

typedef struct _per_run_data {
    uint32_t a;
    uint32_t b;
    uint32_t stopped;
    uint32_t ignoreWakeUps;
} _per_run_data;
/*
 typedef struct __CFRuntimeBase {
 uintptr_t _cfisa;
 uint8_t _cfinfo[4];
 #if __LP64__
 uint32_t _rc;
 #endif
 } CFRuntim
 */
/*
 CFRuntimeBase    _base    應(yīng)該是 Core Foundation 對象都需要的東西
 pthread_mutex_t    _lock    一個(gè) mutex幢码,根據(jù)注釋笤休,是用來鎖對于 mode 的訪問的。對其操作由 __CFRunLoopLockInit症副、__CFRunLoopLock 和 __CFRunLoopUnlock 函數(shù)封裝
 __CFPort    _wakeUpPort    __CFPort 實(shí)際上就是 mach_port_t店雅。根據(jù)注釋政基,這是用來喚醒 run loop 的 mach port,被 CFRunLoopWakeUp 函數(shù)使用
 Boolean    _unused    和變量名一樣底洗,沒有使用的變量腋么,猜測是為了對齊用?
 _per_run_data *    _perRunData    每次調(diào)用 CFRunLoopRun 或者 CFRunLoopRunInMode 函數(shù)(實(shí)際就是對 CFRunLoopRunSpecific 函數(shù)的調(diào)用)亥揖,也就是每次 run 的一個(gè)獨(dú)立數(shù)據(jù)珊擂。相關(guān)操作:__CFRunLoopPushPerRunData 和 __CFRunLoopPopPerRunData,這個(gè)又 push 又 pop 的原因是因?yàn)?run loop 可以嵌套調(diào)用
 pthread_t    _pthread    對應(yīng)的 pthread
 uint32_t    _winthread    Windows 下對應(yīng)線程
 CFMutableSetRef    _commonModes    存放 common mode 的集合
 CFMutableSetRef    _commonModeItems    每個(gè) common mode 都有的 item (source, timer and observer) 集合
 CFRunLoopModeRef    _currentMode    當(dāng)前 run 的 mode
 CFMutableSetRef    _modes    這個(gè) run loop 所有的 mode 集合
 sturct _block_item *    _blocks_head    存放 CFRunLoopPerformBlock 函數(shù)添加的 block 的雙向鏈表的頭指針
 sturct _block_item *    _blocks_tail    同上尾脂針
 CFAbsoluteTime    _runTime    估計(jì)是一共跑著的時(shí)間(但實(shí)際上根本沒使用)
 CFAbsoluteTime    _sleepTime    一共睡了多久
 CFTypeRef    _counterpart    給 Swift 用的玩意
 */
struct __CFRunLoop {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;          /* locked for accessing mode list */
    __CFPort _wakeUpPort;           // used for CFRunLoopWakeUp 
    Boolean _unused;
    volatile _per_run_data *_perRunData;              // reset for runs of the run loop
    pthread_t _pthread;
    uint32_t _winthread;
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
    struct _block_item *_blocks_head;
    struct _block_item *_blocks_tail;
    CFAbsoluteTime _runTime;
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;

除一些記錄性屬性外费变,主要來看一下以下兩個(gè)成員變量
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
CFRunLoopModeRef 其實(shí)是指向__CFRunLoopMode結(jié)構(gòu)體的指針摧扇,__CFRunLoopMode結(jié)構(gòu)體源碼如下

typedef struct __CFRunLoopMode *CFRunLoopModeRef;
/*
 CFRuntimeBase    _base    應(yīng)該是 Core Foundation 對象都需要的東西
 pthread_mutex_t    _lock    一個(gè) mutex扛稽,鎖 mode 里的各種操作滑负。根據(jù)注釋在张,需要 run loop 的鎖先鎖上才能鎖這個(gè)鎖。同樣也有兩個(gè)函數(shù) __CFRunLoopModeLock 和 __CFRunLoopModeUnlock 對其操作進(jìn)行了簡單封裝
 CFStringRef    _name    當(dāng)然是名字啦
 Boolean    _stopped    是否停止了
 char[3]    _padding    可能是為了對齊用的吧……
 CFMutableSetRef    _sources0    Source0 集合矮慕,也就是非 port 的 source
 CFMutableSetRef    _sources1    Source1 集合帮匾,也就是基于 port 的 source
 CFMutableArrayRef    _observers    Observer 集合
 CFMutableArrayRef    _timers    Timer 集合
 CFMutableDictionaryRef    _portToV1SourceMap    Key 是 port,value 是對應(yīng) source1 的字典
 __CFPortSet    _portSet    所有 port 的集合
 CFIndex    _observerMask    需要 observe 的事件的 mask痴鳄,一個(gè)小優(yōu)化
 dispatch_source_t    _timerSource    用來實(shí)現(xiàn) timer 的 GCD timer
 dispatch_queue_t    _queue    放 _timerSource 的隊(duì)列
 Boolean    _timerFired    _timerSource 是否被啟動
 Boolean    _dispatchTimerArmed    timer 是否被安裝上了瘟斜,或者說是否開啟了
 __CFPort    _timerPort    使用 MK timer 時(shí)的端口
 Boolean    _mkTimerArmed    timer 是否被開啟
 uint64_t    _timerSoftDeadline    下一個(gè)計(jì)劃啟動的時(shí)間
 uint64_t    _timerHardDeadline    下一個(gè)最遲啟動的時(shí)間(計(jì)劃加上容忍延遲的時(shí)間)
 */
struct __CFRunLoopMode {
    CFRuntimeBase _base;
    pthread_mutex_t _lock;  /* must have the run loop locked before locking this */
    CFStringRef _name;
    Boolean _stopped;
    char _padding[3];
    CFMutableSetRef _sources0;
    CFMutableSetRef _sources1;
    CFMutableArrayRef _observers;
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    __CFPortSet _portSet;
    CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    dispatch_source_t _timerSource;
    dispatch_queue_t _queue;
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
    uint64_t _timerSoftDeadline; /* TSR */
    uint64_t _timerHardDeadline; /* TSR 
};

RunLoop 對外的接口

在 CoreFoundation 里面關(guān)于 RunLoop 有5個(gè)類:

CFRunLoopRef

CFRunLoopModeRef

CFRunLoopSourceRef

CFRunLoopTimerRef

CFRunLoopObserverRef

其中 CFRunLoopModeRef 類并沒有對外暴露,只是通過 CFRunLoopRef 的接口進(jìn)行了封裝痪寻。他們的關(guān)系如下:

image

通過上面分析我們知道螺句,CFRunLoopModeRef代表RunLoop的運(yùn)行模式,一個(gè) RunLoop 包含若干個(gè) Mode,每個(gè) Mode 又包含若干個(gè) Source/Timer/Observer橡类。每次調(diào)用 RunLoop 的主函數(shù)時(shí)蛇尚,只能指定其中一個(gè) Mode,這個(gè)Mode被稱作 CurrentMode顾画。如果需要切換 Mode取劫,只能退出 Loop,再重新指定一個(gè) Mode 進(jìn)入勇凭。這樣做主要是為了分隔開不同組的 Source/Timer/Observer寓盗,讓其互不影響。

CFRunLoopSourceRef 是事件產(chǎn)生的地方。Source有兩個(gè)版本:Source0 和 Source1库继。

? Source0 只包含了一個(gè)回調(diào)(函數(shù)指針),它并不能主動觸發(fā)事件拜英。使用時(shí)居凶,你需要先調(diào)用 CFRunLoopSourceSignal(source),將這個(gè) Source 標(biāo)記為待處理,然后手動調(diào)用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop挨队,讓其處理這個(gè)事件。

? Source1 包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針)腾夯,被用于通過內(nèi)核和其他線程相互發(fā)送消息。這種 Source 能主動喚醒 RunLoop 的線程,其原理在下面會講到积蜻。

CFRunLoopTimerRef 是基于時(shí)間的觸發(fā)器宙拉,它和 NSTimer 是toll-free bridged 的,可以混用澳化。其包含一個(gè)時(shí)間長度和一個(gè)回調(diào)(函數(shù)指針)。當(dāng)其加入到 RunLoop 時(shí),RunLoop會注冊對應(yīng)的時(shí)間點(diǎn)希痴,當(dāng)時(shí)間點(diǎn)到時(shí),RunLoop會被喚醒以執(zhí)行那個(gè)回調(diào)。

CFRunLoopObserverRef 是觀察者甲献,每個(gè) Observer 都包含了一個(gè)回調(diào)(函數(shù)指針),當(dāng) RunLoop 的狀態(tài)發(fā)生變化時(shí),觀察者就能通過回調(diào)接受到這個(gè)變化吃引」美叮可以觀測的時(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)稱為 mode item旭愧,一個(gè) item 可以被同時(shí)加入多個(gè) mode。但一個(gè) item 被重復(fù)加入同一個(gè) mode 時(shí)是不會有效果的。如果一個(gè) mode 中一個(gè) item 都沒有瞳收,則 RunLoop 會直接退出,不進(jìn)入循環(huán)。

RunLoop 的內(nèi)部邏輯

根據(jù)蘋果在文檔里的說明垢箕,RunLoop 內(nèi)部的邏輯大致如下:

image

官方文檔:

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啟動
void CFRunLoopRun(void) {

    CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);

}
/// 用指定的Mode啟動,允許設(shè)置RunLoop超時(shí)時(shí)間
int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {

    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);

}

以下是官方源碼 并沒有刪減代碼,這樣大家更好理解點(diǎn)

SInt32 CFRunLoopRunSpecific(CFRunLoopRefrl,CFStringRefmodeName,CFTimeIntervalseconds,BooleanreturnAfterSourceHandled) {    /* DOES CALLOUT */

    CHECK_FOR_FORK();

     //檢查 run loop 是否正在銷毀

    if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;

    __CFRunLoopLock(rl);

    // 查找 modeName 指定的 mode

    CFRunLoopModeRefcurrentMode =__CFRunLoopFindMode(rl, modeName,false);

    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {// 沒有找到 mode 或者 mode 里面沒有任何事件源的話,返回 kCFRunLoopRunFinished

        //這里比較奇怪的是 Boolean did = false 直接寫死了 did 的值,后面又是 return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished . 懷疑 did 的值癣诱,應(yīng)該還有一段代碼是決定kCFRunLoopRunHandledSource的結(jié)果,被蘋果隱藏了沒有開源出來

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對象

 *  @param rlm            運(yùn)行的mode

 *  @param seconds        run loop超時(shí)時(shí)間

 *  @param stopAfterHandle true:run loop處理完事件就退出  false:一直運(yùn)行直到超時(shí)或者被手動終止

 *  @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/總線依賴函數(shù),返回一個(gè)基于系統(tǒng)啟動后的時(shí)鐘"嘀嗒"數(shù)六水。在macOS上可以確保它的行為添寺,并且,它包含系統(tǒng)時(shí)鐘所包含的所有時(shí)間區(qū)域票罐。其可獲取納秒級的精度

    //獲取系統(tǒng)啟動之后cpu嘀嗒數(shù)

    uint64_t startTSR =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ù)端口名稱空間的信息。

     該mach_port_names返回有關(guān)任務(wù)的端口名稱空間腊尚。它返回任務(wù)的當(dāng)前活動名稱吨拗,表示某些端口,端口集或死命名跟伏。對于每個(gè)名稱,它還返回任何類型的權(quán)限 任務(wù)(由mach_port_type返回的相同信息)翩瓜。

     請注意,當(dāng)對mach_port_names的調(diào)用返回時(shí)夸楣,兩個(gè)輸出數(shù)組(名稱和類型)中的條目數(shù)相等(namesCnt等于typesCnt)涉兽。此接口返回兩個(gè)單獨(dú)計(jì)數(shù)的事實(shí)是Mach Interface Generator的工件袋倔。

     task

     查詢端口名稱空間的任務(wù)食零。

     names

     [指向動態(tài)數(shù)組mach_port_name_t的指針]任務(wù)端口名稱空間中的端口,端口集和死名稱的名稱婚苹,沒有特定的順序瓜挽。

     namesCnt

     [out scalar]返回的名稱數(shù)量甚带。

     types

     [指向動態(tài)數(shù)組mach_port_type_t的指針]每個(gè)相應(yīng)名稱的類型聚假。指示任務(wù)使用該名稱保留的權(quán)限類型。

     typesCnt

     [out scalar]返回的類型數(shù)主慰。

     返回值

     僅適用一般性錯誤。

     */

    //mach 端口, 線程之間通信的對象  mach端口蒲赂,內(nèi)核進(jìn)程通信消息端口走触。初始為0

    mach_port_name_t dispatchPort =MACH_PORT_NULL;

    //pthread_main_np() 獲取主線程 這里主要是為了判斷當(dāng)前線程是否為主線程 

    // 檢測是否在主線程 && ( (是隊(duì)列發(fā)的消息&&mode為null)||(不是隊(duì)列發(fā)的消息&&不在主隊(duì)列))

    Boolean libdispatchQSafe = 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è)置它對應(yīng)的通信端口

    /*

     _dispatch_get_main_queue_port_4CF是 dispatch_get_main_queue_handle_4CF 的宏,存在 libdispatch 中传藏,里面對它的實(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领炫,也就是說有使用 GCD 來實(shí)現(xiàn) timer偶垮,當(dāng)然 USE_MK_TIMER_TOO 這個(gè)宏的值也是 1张咳,表示也使用了更底層的 timer帝洪。

     */

    //如果使用 GCD timer 作為 timer 的實(shí)現(xiàn)的話,進(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ì)列還是后臺隊(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)用程序定義的上下文與對象相關(guān)聯(lián)砰奕。

         您的應(yīng)用程序可以將自定義上下文數(shù)據(jù)與對象相關(guān)聯(lián),僅供您的應(yīng)用程序使用提鸟。您的應(yīng)用程序必須根據(jù)需要分配和取消分配數(shù)據(jù)军援。

         object

         這個(gè)參數(shù)不能NULL。

         context

         對象的新應(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_t ns_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í)

        //無限期超時(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_t retVal =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;

        // 活動端口宠哄,本地用來接收消息的端口

        mach_port_t livePort = MACH_PORT_NULL;

#endif

        // 取所有需要監(jiān)聽的port,runloopMode

        // 聲明一個(gè)類型為 CFPortSet 的 waitSet, 值為 run loop mode 里的 portSet.

        __CFPortSet waitSet = rlm->_portSet;

        // 設(shè)置RunLoop為可以被喚醒狀態(tài)

        //將 run loop 從忽略喚醒消息的狀態(tài) unset ,開始接受喚醒消息

        __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瞭恰,沒有事件返回 false

        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);

        if(sourceHandledThisLoop) {

            // 執(zhí)行加入當(dāng)前 runloop 的 block

            __CFRunLoopDoBlocks(rl, rlm);

        }

        // 如果沒有 Sources0 事件處理 并且 沒有超時(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)不會走該分支,因?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)) {

                // 如果接收到了消息的話,前往 handle_msg 開始處理 msg

                gotohandle_msg;

            }

#endif

        }

        didDispatchPortLastTime =false;

        //6, 通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)

        // 注意到如果實(shí)際處理了 source0 或者超時(shí)浪讳,不會進(jìn)入睡眠缰盏,所以不會通知。

        if(!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);

        //設(shè)置標(biāo)志位淹遵,正在睡眠(實(shí)際上沒有開始睡)

        // 設(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 的話,將 GCD 端口加入所有監(jiān)聽端口集合中

        __CFPortSetInsert(dispatchPort, waitSet);

        __CFRunLoopModeUnlock(rlm);

        __CFRunLoopUnlock(rl);

        //使用 GCD 的話透揣,將 GCD 端口加入所有監(jiān)聽端口集合中

        // 休眠開始的時(shí)間济炎,根據(jù) poll 狀態(tài)決定為 0 或者當(dāng)前的絕對時(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.通過 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ù)會睡眠線程 waitSet: 監(jiān)聽端口集合  livePort: 返回收到消息的端口 poll: 根據(jù)狀態(tài)睡眠或者不睡

            // 接收waitSet端口的消息

            // 設(shè)置 mach port 通信撩轰,會睡眠線程

            __CFRunLoopServiceMachPort(waitSet, &msg,sizeof(msg_buffer), &livePort, poll ?0: TIMEOUT_INFINITY, &voucherState, &voucherCopy);

            // 如果是 timer 端口喚醒的,進(jìn)行一下善后處理,之后再處理 timer

            // 收到消息之后钧敞,livePort 的值為本地接收消息的活動端口

            // modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);

            //modeQueuePort:如果使用 GCD timer 作為 timer 的實(shí)現(xiàn)的話蜡豹,進(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)行接下來的處理

                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)用者手動喚醒

        msg = (mach_msg_header_t *)msg_buffer;

        //CFRunLoopServiceMachPort 會讓線程休眠

        __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 端口移除

        //對 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í)際處理過 source 0 或者已經(jīng)超時(shí)的話想许,不會通知(因?yàn)闆]有睡)

        if(!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

        // 11\. 被什么喚醒就處理什么: 處理通過端口收到的消息

    handle_msg:;

        // 設(shè)置 runloop 不可被喚醒

        //將 Run Loop 重新忽略喚醒消息,因?yàn)橐呀?jīng)重新在運(yùn)行了

        __CFRunLoopSetIgnoreWakeUps(rl);

        if (MACH_PORT_NULL == livePort) {// 不知道哪個(gè)端口喚醒的(或者根本沒睡)断序,啥也不干  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)過來的話阵苇,處理 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 會在 TSD 中去查找這個(gè) voucher. 

            // 通過使用 TSD 中的值,我們將 CFMachPortBoost 綁定到這個(gè)接收到的 mach_msg 中慎玖,在這兩段代碼之間沒有任何機(jī)會再次設(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 {//不存在,將對應(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í)鳍侣,線程就會一直停留在這個(gè)循環(huán)里丁稀;直到超時(shí)或被手動停止,該函數(shù)才會返回倚聚。

對__CFRunLoopRun方法細(xì)聊

1,開始獲取當(dāng)前時(shí)間點(diǎn)
2, __CFRunLoopIsStopped判斷是否已經(jīng)停止 停止就返回停止?fàn)顟B(tài)出去
3 聲明一個(gè) mach_port 通信的端口dispatchPort ,如果是主線程就將主線程端口賦值給dispatchPort,dispatchPort = _dispatch_get_main_queue_port_4CF();
4,MACOSX 下线衫,聲明一個(gè) mode 的隊(duì)列通信端口(在 MACOSX 環(huán)境中):modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
5,用GCD管理runloop超時(shí)機(jī)制
定時(shí)器開始調(diào)用事件__CFRunLoopTimeout

static void __CFRunLoopTimeout(void *arg) {
    struct __timeout_context *context = (struct __timeout_context *)arg;
    context->termTSR = 0ULL;
    CFRUNLOOP_WAKEUP_FOR_TIMEOUT();
    //等待runloop消息
    CFRunLoopWakeUp(context->rl);
    // The interval is DISPATCH_TIME_FOREVER, so this won't fire again
}

6,進(jìn)入do while循環(huán):
1),聲明狀態(tài)變量用于消息狀態(tài)標(biāo)志

 voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;

2),初始化一個(gè)存放內(nèi)核消息緩存池

uint8_t msg_buffer[3 * 1024];

3),聲明和 mach port 有關(guān)的 port 和 msg 變量

mach_msg_header_t *msg = NULL;

活動端口,本地用來接收消息的端口

 mach_port_t livePort = MACH_PORT_NULL;

4),取所有需要監(jiān)聽的port,runloopMode

__CFPortSet waitSet = rlm->_portSet;

二,通知 Observers: RunLoop 即將觸發(fā) Timer 回調(diào)

if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);

三 通知 Observers: RunLoop 即將觸發(fā) Source0 (非port) 回調(diào)惑折。

if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

__CFRunLoopDoObservers里面調(diào)用方法調(diào)用系統(tǒng)方法執(zhí)行回調(diào)出去

 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);

5),執(zhí)行被加入的block

__CFRunLoopDoBlocks(rl, rlm);

block執(zhí)行實(shí)際上是調(diào)用方法執(zhí)行block的

  __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);

四,RunLoop 觸發(fā) Source0 (非port) 回調(diào)授账。

 Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            // 執(zhí)行加入當(dāng)前 runloop 的 block
           //執(zhí)行完 source0 后,假如還有需要執(zhí)行的惨驶,再執(zhí)行一次 block
            __CFRunLoopDoBlocks(rl, rlm);
    }

__CFRunLoopDoSources0代碼實(shí)際調(diào)用系統(tǒng)方法

 if (__CFIsValid(rls)) {
                __CFRunLoopSourceUnlock(rls);
                    __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
                CHECK_FOR_FORK();
                sourceHandled = true;
            } else {
                __CFRunLoopSourceUnlock(rls);
            }

五,如果有 Source1 (基于port) 處于 ready 狀態(tài)白热,直接處理這個(gè) Source1 然后跳轉(zhuǎn)去處理消息

// 從緩沖區(qū)讀取消息
            msg = (mach_msg_header_t *)msg_buffer;
              // 5. 接收 dispatchPort 端口的消息,dispatch 到 main queue 的事件粗卜。
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
                 // 如果接收到了消息的話屋确,前往 handle_msg 開始處理 msg
                goto handle_msg;
            }

對于__CFRunLoopServiceMachPort方法 詳細(xì)介紹一下,后面會還有多個(gè)地方用到的

/**
 *  接收指定內(nèi)核端口的消息
 *
 *  @param port        接收消息的端口
 *  @param buffer      消息緩沖區(qū)
 *  @param buffer_size 消息緩沖區(qū)大小
 *  @param livePort    暫且理解為活動的端口,接收消息成功時(shí)候值為msg->msgh_local_port续扔,超時(shí)時(shí)為MACH_PORT_NULL
 *  @param timeout     超時(shí)時(shí)間乍恐,單位是ms,如果超時(shí)测砂,則RunLoop進(jìn)入休眠狀態(tài)
 *
 *  @return 接收消息成功時(shí)返回true 其他情況返回false
 */
static Boolean __CFRunLoopServiceMachPort(mach_port_name_t port, mach_msg_header_t **buffer, size_t buffer_size, mach_port_t *livePort, mach_msg_timeout_t timeout, voucher_mach_msg_state_t *voucherState, voucher_t *voucherCopy) {
    Boolean originalBuffer = true;
    kern_return_t ret = KERN_SUCCESS;
    for (;;) {      /* In that sleep of death what nightmares may come ... */
        mach_msg_header_t *msg = (mach_msg_header_t *)*buffer;
        msg->msgh_bits = 0;// 消息頭的標(biāo)志位
        msg->msgh_local_port = port;// 本地消息端口
        msg->msgh_remote_port = MACH_PORT_NULL;// 目標(biāo)消息端口
        msg->msgh_size = buffer_size;// 消息緩沖區(qū)大小
        msg->msgh_id = 0;// msg id
        // 根據(jù) timeout 的值茵烈,決定 Run Loop 是休眠還是執(zhí)行
        // timeout  為 TIMEOUT_INFINITY 時(shí),才執(zhí)行 CFRUNLOOP_SLEEP() 休眠
        if (TIMEOUT_INFINITY == timeout) { CFRUNLOOP_SLEEP(); } else { CFRUNLOOP_POLL(); }
         // 系統(tǒng)底層處理消息的機(jī)制
        // 發(fā)送并接收 mach port 消息
        ret = mach_msg(msg, MACH_RCV_MSG|(voucherState ? MACH_RCV_VOUCHER : 0)|MACH_RCV_LARGE|((TIMEOUT_INFINITY != timeout) ? MACH_RCV_TIMEOUT : 0)|MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)|MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AV), 0, msg->msgh_size, port, timeout, MACH_PORT_NULL);

        /*!
          extern void voucher_mach_msg_revert(voucher_mach_msg_state_t state);
         * @function voucher_mach_msg_revert
         *
         * @abstract
         * Restore thread voucher state previously modified by voucher_mach_msg_adopt().
         *
         * @discussion
         * Current thread voucher reference is released.
         * No change to thread voucher state if passed VOUCHER_MACH_MSG_STATE_UNCHANGED.
         *
         * @param state
         * The thread voucher state to restore.
         */
       
        // Take care of all voucher-related work right after mach_msg.
        // If we don't release the previous voucher we're going to leak it.
        // 在 mach_msg 之后注意所有 voucher 相關(guān)的正常運(yùn)行
        // 假如我們沒有釋放前面的 voucher , 將會出現(xiàn)內(nèi)存泄漏
        voucher_mach_msg_revert(*voucherState);
        
        /*!
         extern voucher_mach_msg_state_t voucher_mach_msg_adopt(mach_msg_header_t *msg);
         * @function voucher_mach_msg_adopt
         *
         * @abstract
         * Adopt the voucher contained in the specified message on the current thread
         * and return the previous thread voucher state.
         有關(guān)線程的信息
         *
         * @discussion
         * Ownership of the mach voucher in the message is transferred to the current
         * thread and the message header voucher fields are cleared.
         *
         * @param msg
         * The message to query and modify.
         *
         * @result
         * The previous thread voucher state or VOUCHER_MACH_MSG_STATE_UNCHANGED if no
         * state change occurred.
         
         */
        // Someone will be responsible for calling voucher_mach_msg_revert. This call makes the received voucher the current one.
          // 會有調(diào)用者去負(fù)責(zé)調(diào)用 voucher_mach_msg_revert .它會讓接收到的 voucher 變成當(dāng)前的這一個(gè)值砌些。
        *voucherState = voucher_mach_msg_adopt(msg);
        
        if (voucherCopy) {
            if (*voucherState != VOUCHER_MACH_MSG_STATE_UNCHANGED) {
                // Caller requested a copy of the voucher at this point. By doing this right next to mach_msg we make sure that no voucher has been set in between the return of mach_msg and the use of the voucher copy.
                // CFMachPortBoost uses the voucher to drop importance explicitly. However, we want to make sure we only drop importance for a new voucher (not unchanged), so we only set the TSD when the voucher is not state_unchanged.
                // 調(diào)用者 在這里 請求了一個(gè) voucher 的復(fù)制的值呜投。通過在 mach_msg 前后做這個(gè)操作,我們確保在 mach_msg 返回和使用 voucher 的復(fù)制值的時(shí)候存璃,沒有涉及設(shè)置 voucher 的值仑荐。
                // 為確保  CFMachPortBoost 使用的  voucher ,所以我們只在 voucher 不是  state_unchanged 的時(shí)候纵东,去設(shè)置 TSD 粘招。
                *voucherCopy = voucher_copy();
            } else {
                *voucherCopy = NULL;
            }
        }

        /*
         void CFRunLoopWakeUp(CFRunLoopRef rl);
         Wakes a waiting CFRunLoop object.
         A run loop goes to sleep when it is waiting for a source or timer to become ready to fire. If no source or timer fires, the run loop stays there until it times out or is explicitly woken up. If a run loop is modified, such as a new source added, you need to wake up the run loop to allow it to process the change. Version 0 sources use CFRunLoopWakeUp to cause the run loop to wake up after setting a source to be signaled, if they want the source handled immediately.
         運(yùn)行循環(huán)在等待源或計(jì)時(shí)器準(zhǔn)備好觸發(fā)時(shí)進(jìn)入休眠狀態(tài)。如果沒有源或計(jì)時(shí)器觸發(fā)偎球,則運(yùn)行循環(huán)將保持不變洒扎,直到超時(shí)或顯式喚醒為止。如果修改了運(yùn)行循環(huán)衰絮,例如添加了新的源袍冷,則需要喚醒運(yùn)行循環(huán)以允許它處理更改。版本0源用于在設(shè)置要發(fā)信號的源之后喚醒運(yùn)行循環(huán)猫牡,如果他們想要立即處理源胡诗。CFRunLoopWakeUp
         rl
         The run loop to wake up.
         */
         // 喚醒 Run Loop
        CFRUNLOOP_WAKEUP(ret);
        if (MACH_MSG_SUCCESS == ret) { // 接收/發(fā)送消息成功,給 livePort 賦值為 msgh_local_port
            *livePort = msg ? msg->msgh_local_port : MACH_PORT_NULL;
            return true;
        }
        if (MACH_RCV_TIMED_OUT == ret) {// 接收消息超時(shí)
            if (!originalBuffer) free(msg);
            *buffer = NULL;
            *livePort = MACH_PORT_NULL;
            return false;
        }
        if (MACH_RCV_TOO_LARGE != ret) break;
          // 消息體過大,分配更多的內(nèi)存
        buffer_size = round_msg(msg->msgh_size + MAX_TRAILER_SIZE);
        if (originalBuffer) *buffer = NULL;
        originalBuffer = false;
        *buffer = realloc(*buffer, buffer_size);
    }
    HALT;
    return false;
}

可以看出里面調(diào)用系統(tǒng)mach_msg方法煌恢,mach_msg() 函數(shù)實(shí)際上是調(diào)用了一個(gè) Mach 陷阱 (trap)骇陈,即函數(shù)mach_msg_trap(),陷阱這個(gè)概念在 Mach 中等同于系統(tǒng)調(diào)用瑰抵。當(dāng)你在用戶態(tài)調(diào)用 mach_msg_trap() 時(shí)會觸發(fā)陷阱機(jī)制缩歪,切換到內(nèi)核態(tài);內(nèi)核態(tài)中內(nèi)核實(shí)現(xiàn)的 mach_msg() 函數(shù)會完成實(shí)際的工作谍憔,如下圖:


image.png

六,通知 Observers: RunLoop 的線程即將進(jìn)入休眠(sleep)

     // 注意到如果實(shí)際處理了 source0 或者超時(shí)匪蝙,不會進(jìn)入睡眠,所以不會通知习贫。
    if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        //設(shè)置標(biāo)志位逛球,正在睡眠(實(shí)際上沒有開始睡)
       // 設(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 的話苫昌,將 GCD 端口加入所有監(jiān)聽端口集合中
        __CFPortSetInsert(dispatchPort, waitSet);

6)記錄開始休眠時(shí)間

  //使用 GCD 的話颤绕,將 GCD 端口加入所有監(jiān)聽端口集合中
        // 休眠開始的時(shí)間,根據(jù) poll 狀態(tài)決定為 0 或者當(dāng)前的絕對時(shí)間
        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();

七,通過 CFRunLoopServiceMachPort 調(diào)用 mach_msg 休眠,等待被 mach_msg 消息喚醒

// 不使用 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)用者手動喚醒
        msg = (mach_msg_header_t *)msg_buffer;
        //CFRunLoopServiceMachPort 會讓線程休眠
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

由于字?jǐn)?shù)有限制 沒有寫下一下 下一篇續(xù)http://www.reibang.com/p/3bcf780d55c3

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末氯葬,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子婉陷,更是在濱河造成了極大的恐慌帚称,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秽澳,死亡現(xiàn)場離奇詭異闯睹,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)担神,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門楼吃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人妄讯,你說我怎么就攤上這事孩锡。” “怎么了捞挥?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵浮创,是天一觀的道長。 經(jīng)常有香客問我砌函,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任讹俊,我火速辦了婚禮垦沉,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘仍劈。我一直安慰自己厕倍,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布贩疙。 她就那樣靜靜地躺著讹弯,像睡著了一般。 火紅的嫁衣襯著肌膚如雪这溅。 梳的紋絲不亂的頭發(fā)上组民,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天,我揣著相機(jī)與錄音悲靴,去河邊找鬼臭胜。 笑死,一個(gè)胖子當(dāng)著我的面吹牛癞尚,可吹牛的內(nèi)容都是我干的耸三。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼浇揩,長吁一口氣:“原來是場噩夢啊……” “哼仪壮!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胳徽,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤睛驳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后膜廊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體乏沸,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年爪瓜,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蹬跃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡铆铆,死狀恐怖蝶缀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情薄货,我是刑警寧澤翁都,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站谅猾,受9級特大地震影響柄慰,放射性物質(zhì)發(fā)生泄漏鳍悠。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一坐搔、第九天 我趴在偏房一處隱蔽的房頂上張望藏研。 院中可真熱鬧,春花似錦概行、人聲如沸蠢挡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽业踏。三九已至,卻和暖如春涧卵,著一層夾襖步出監(jiān)牢的瞬間勤家,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工艺演, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留却紧,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓胎撤,卻偏偏與公主長得像晓殊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子伤提,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容