iOS 查漏補(bǔ)缺 - RunLoop

iOS 查漏補(bǔ)缺 - RunLoop.png

RunLoop 在 0202 年的今天其實(shí)已經(jīng)不是個(gè)新鮮的話題了且改,關(guān)于這方面的文章網(wǎng)上有很多大神總結(jié)得非常精辟拘央。

作為 iOS 查漏補(bǔ)缺系列斋日,這篇文章是筆者探索 RunLoop 底層的一些知識(shí)點(diǎn)總結(jié)渴杆,同時(shí)也借鑒了網(wǎng)上一些優(yōu)秀的 RunLoop 技術(shù)文章的內(nèi)容林艘。

本文內(nèi)容如有錯(cuò)誤盖奈,歡迎指正。

RunLoop 前導(dǎo)知識(shí)

iOS/OS X 系統(tǒng)架構(gòu)

iOS 進(jìn)化史

image.png

Mac OS Classic 擁有偉大的 GUI狐援,但系統(tǒng)設(shè)計(jì)卻非常糟糕钢坦,尤其是協(xié)作式的多任務(wù)系統(tǒng)和低效的內(nèi)存管理,以今天的標(biāo)準(zhǔn)看非常原始啥酱。

NeXTSTEP 操作系統(tǒng)作為喬布斯回歸蘋(píng)果的嫁妝爹凹,連同架構(gòu)中的 Mach 和 OC 等一起融合進(jìn)了Mac OS Classic。與其說(shuō)是 Mac OS Classic 融合了 NeXTSTEP镶殷,不如說(shuō)是后者反客為主替換了前者禾酱,這個(gè)轉(zhuǎn)變并不是一瞬間發(fā)生的。

Mac OS Classic 擁有不錯(cuò)的 GUI 但系統(tǒng)設(shè)計(jì)糟糕绘趋,NeXTSTEP 設(shè)計(jì)很棒颤陶,但 GUI 非常平淡。這兩個(gè)小眾操作系統(tǒng)融合的結(jié)果是一個(gè)繼承二者優(yōu)點(diǎn)的成功得多的全新操作系統(tǒng)—Mac OS X
全新的 Mac OS X 在設(shè)計(jì)與實(shí)現(xiàn)上都同 NeXTSTEP 非常接近陷遮,諸如 Cocoa滓走、Mach 、Interface Builder 等核心組件都源自 NeXTSTEP

iOS 最初稱為 iPhone OS,是 Mac OS X 應(yīng)對(duì)移動(dòng)平臺(tái)的分支帽馋,iOS 擁有和 Mac OS X一樣的操作系統(tǒng)層次結(jié)構(gòu)以及相同的操作系統(tǒng)核心Dawin搅方。

iOS 系統(tǒng)架構(gòu)

image.png

蘋(píng)果官方將整個(gè)系統(tǒng)大致劃分為上述4個(gè)層次:

應(yīng)用層包括用戶能接觸到的圖形應(yīng)用,例如 Spotlight绽族、Aqua(macOS)姨涡、SpringBoard(iOS) 等。

應(yīng)用框架層即開(kāi)發(fā)人員接觸到的 Cocoa 等框架项秉。

核心框架層包括各種核心框架绣溜、OpenGL 等內(nèi)容。

Darwin層是操作系統(tǒng)核心娄蔼,包括 XNU 內(nèi)核(2017 年已開(kāi)源)怖喻、驅(qū)動(dòng)和 UNIX shell

Darwin 架構(gòu)

image.png

Darwin 的內(nèi)核是 XNU底哗,XNU is Not Unix。XNU 是兩種技術(shù)的混合體锚沸,Mach和BSD跋选。BSD 層確保了 Darwin 系統(tǒng)的 UNIX 特性,真正的內(nèi)核是 Mach哗蜈,但是對(duì)外部隱藏前标。BSD 以上屬于用戶態(tài),所有的內(nèi)容都可以被應(yīng)用程序訪問(wèn)距潘,而應(yīng)用程序不能訪問(wèn)內(nèi)核態(tài)炼列。當(dāng)需要從用戶態(tài)切換到內(nèi)核態(tài)的時(shí)候,需要通過(guò) mach trap 實(shí)現(xiàn)切換音比。

---《深入解析Mac OS X & iOS 操作系統(tǒng)》

其中俭尖,在硬件層上面的三個(gè)組成部分:Mach、BSD洞翩、IOKit (還包括一些上面沒(méi)標(biāo)注的內(nèi)容)稽犁,共同組成了 XNU 內(nèi)核。

XNU 內(nèi)核的內(nèi)環(huán)被稱作 Mach骚亿,其作為一個(gè)微內(nèi)核已亥,僅提供了諸如處理器調(diào)度、IPC (進(jìn)程間通信)等非常少量的基礎(chǔ)服務(wù)来屠。

BSD 層可以看作圍繞 Mach 層的一個(gè)外環(huán)虑椎,其提供了諸如進(jìn)程管理、文件系統(tǒng)和網(wǎng)絡(luò)等功能俱笛。

IOKit 層是為設(shè)備驅(qū)動(dòng)提供了一個(gè)面向?qū)ο?C++)的一個(gè)框架绣檬。

Mach 本身提供的 API 非常有限,而且蘋(píng)果也不鼓勵(lì)使用 Mach 的 API嫂粟,但是這些API非辰课矗基礎(chǔ),如果沒(méi)有這些API的話星虹,其他任何工作都無(wú)法實(shí)施零抬。
在 Mach 中,所有的東西都是通過(guò)自己的對(duì)象實(shí)現(xiàn)的宽涌,進(jìn)程平夜、線程和虛擬內(nèi)存都被稱為”對(duì)象”。

和其他架構(gòu)不同卸亮, Mach 的對(duì)象間不能直接調(diào)用忽妒,只能通過(guò)消息傳遞的方式實(shí)現(xiàn)對(duì)象間的通信。

”消息”是 Mach 中最基礎(chǔ)的概念,消息在兩個(gè)端口 (port) 之間傳遞段直,這就是 Mach 的 IPC (進(jìn)程間通信) 的核心吃溅。

--- 《深入理解RunLoop》ibireme

mach_msg 函數(shù)

mach_msg 函數(shù)位于 mach 內(nèi)核的 message.h 頭文件中

image.png

這是系統(tǒng)內(nèi)核在某個(gè) port 收發(fā)消息所使用的函數(shù),理解這個(gè)函數(shù)對(duì)于理解 runloop 的運(yùn)行機(jī)制非常重要鸯檬。
詳細(xì)的說(shuō)明可參考 這里

// mach 消息基礎(chǔ)結(jié)構(gòu)體
typedef struct{
  // 消息頭部
    mach_msg_header_t       header;
    // 消息體
    mach_msg_body_t         body;
} mach_msg_base_t;

// mach 消息頭結(jié)構(gòu)體
typedef struct{
  // 消息頭的 bits
    mach_msg_bits_t       msgh_bits;
    // 消息頭的大小
    mach_msg_size_t       msgh_size;
    // 遠(yuǎn)程端口號(hào)
    mach_port_t           msgh_remote_port;
    // 本地端口號(hào)
    mach_port_t           msgh_local_port;
    // 憑證端口號(hào)
    mach_port_name_t      msgh_voucher_port;
    // 消息ID
    mach_msg_id_t         msgh_id;
} mach_msg_header_t;

// mach_msg 函數(shù)
mach_msg_return_t mach_msg(
    // 消息頭
    mach_msg_header_t *msg,
    // 消息選項(xiàng)喧务,可用來(lái)指定是發(fā)消息(MACH_SEND_MSG)還是收消息(MACH_RCV_MSG)
    mach_msg_option_t option,
    // 發(fā)消息時(shí)設(shè)置的緩存區(qū)大小赖歌,無(wú)符號(hào)整型
    mach_msg_size_t send_size,
    // 收消息時(shí)設(shè)置的緩存區(qū)大小,無(wú)符號(hào)整型
    mach_msg_size_t rcv_size,
    // 收消息的端口功茴,無(wú)符號(hào)整型
    mach_port_name_t rcv_name,
    // 消息過(guò)期時(shí)間
    // 當(dāng) option 參數(shù)中包含 MACH_SEND_TIMEOUT 或 MACH_RCV_TIMEOUT 的時(shí)候庐冯,傳入以毫秒單位的過(guò)期時(shí)間
    mach_msg_timeout_t timeout,
    // 接收通知的端口
    // 當(dāng) option 參數(shù)中包含 MACH_SEND_CANCEL 和 MACH_RCV_NOTIFY 時(shí),設(shè)置接收通知的端口坎穿;否則肄扎,傳入 MACH_PORT_NULL。
    mach_port_name_t notify);

可以簡(jiǎn)單的將 mach_msg 理解為多進(jìn)程之間的一種通信機(jī)制赁酝,不同的進(jìn)程可以使用同一個(gè)消息隊(duì)列來(lái)交流數(shù)據(jù),當(dāng)使用 mach_msg 從消息隊(duì)列里讀取 msg 時(shí)旭等,可以在參數(shù)中設(shè)置 timeout 值酌呆,在 timeout 之前如果沒(méi)有讀到 msg,當(dāng)前線程會(huì)一直處于休眠狀態(tài)隙袁。這也是 runloop 在沒(méi)有任務(wù)可執(zhí)行的時(shí)候弃榨,能夠進(jìn)入 sleep 狀態(tài)的原因菩收。
--- 《解密 Runloop》MrPeak

為了實(shí)現(xiàn)消息的發(fā)送和接收,mach_msg() 函數(shù)實(shí)際上是調(diào)用了一個(gè) Mach 陷阱 (trap)鲸睛,即函數(shù) mach_msg_trap()娜饵,陷阱這個(gè)概念在 Mach 中等同于系統(tǒng)調(diào)用。當(dāng)你在用戶態(tài)調(diào)用 mach_msg_trap() 時(shí)會(huì)觸發(fā)陷阱機(jī)制官辈,切換到內(nèi)核態(tài)箱舞;內(nèi)核態(tài)中內(nèi)核實(shí)現(xiàn)的 mach_msg() 函數(shù)會(huì)完成實(shí)際的工作。
--- 《深入理解RunLoop》ibireme

CoreFoundation 基礎(chǔ)

CoreFoundation 是一套基于 CAPI拳亿,為 iOS 提供了基本數(shù)據(jù)管理和服務(wù)功能晴股。因?yàn)?CoreFoundation 對(duì)象是基于 C 實(shí)現(xiàn)的,也有引用計(jì)數(shù)的概念怎诫,而 Foundation 對(duì)象是基于 OC 實(shí)現(xiàn)的涌哲。兩種對(duì)象在相互轉(zhuǎn)換的時(shí)候需要用到三個(gè)關(guān)鍵字: bridge初烘、bridge_retained吗铐、__bridge_transfer镊逝,下面進(jìn)行簡(jiǎn)單的介紹减江。

__bridge

id obj = [[NSObject alloc] init];
void *p = (__bridge void *)obj;
id o = (__bridge id)p;

將 Objective-C 的對(duì)象類型用 *bridge 轉(zhuǎn)換為 void 類型和使用 **unsafe_unretained 關(guān)鍵字修飾的變量是一樣的巡莹。

__bridge: 只做類型轉(zhuǎn)換腰根,不修改相關(guān)對(duì)象的引用計(jì)數(shù)东帅,原來(lái)的 Core Foundation 對(duì)象在不用時(shí)坎炼,需要調(diào)用 CFRelease 方法

__bridge_retained

d obj = [[NSObject alloc] init]; 
void *p = (__bridge_retained void *)obj;

從名字上我們應(yīng)該能理解其意義:類型被轉(zhuǎn)換時(shí),其對(duì)象的所有權(quán)也將被變換后變量所持有。<br />如果是 MRC 中:

id obj = [[NSObject alloc] init];
void *p = obj;
[(id)p retain];

下面的例子驗(yàn)證了出了大括號(hào)的范圍后瑞侮,p 仍然指向一個(gè)有效的實(shí)體梅掠。說(shuō)明她擁有該對(duì)象的所有權(quán)都哭,該對(duì)象沒(méi)有因?yàn)槌銎涠x范圍而被銷毀。

void *p = 0;
{
 id obj = [[NSObject alloc] init];
 p = (__bridge_retained void *)obj;
}
NSLog(@"class=%@", [(__bridge id)p class]);

__bridge_retained: 類型轉(zhuǎn)換后,將相關(guān)對(duì)象的引用計(jì)數(shù)加 1,原來(lái)的 Core Foundation 對(duì)象在不用時(shí)迂烁,需要調(diào)用 CFRelease 方法

__bridge_transfer

MRC 中:

// p 變量原先持有對(duì)象的所有權(quán)
id obj = (id)p;
[obj retain];
[(id)p release];

ARC 中:

// p 變量原先持有對(duì)象的所有權(quán)
id obj = (__bridge_transfer id)p;

可以看出來(lái),**bridge_retained 是編譯器替我們做了 retain 操作屈溉,而 **bridge_transfer 是替我們做了 release场航。

__bridge_transfer: 類型轉(zhuǎn)換后孩饼,將該對(duì)象的引用計(jì)數(shù)交給 ARC 管理,Core Foundation 對(duì)象在不用時(shí)竹挡,不需要調(diào)用 CFRelease 方法

Toll-Free bridged

CoreFoundation 和 Foundation 之間有一個(gè) Toll-free bridging 機(jī)制镀娶。<br />在 MRC 時(shí)代,可以直接通過(guò)類型強(qiáng)轉(zhuǎn)完成兩個(gè)框架對(duì)象的類型轉(zhuǎn)換贤重。但是在 ARC 時(shí)代疤祭,則需要使用 Toll-free bridging 機(jī)制提供的方法來(lái)操作。上面已經(jīng)介紹了三種不同的轉(zhuǎn)換機(jī)制,而實(shí)際上在 Foundation 中,有更簡(jiǎn)便的方法來(lái)操作。如下所示:

// After using a CFBridgingRetain on an NSObject, the caller must take responsibility for calling CFRelease at an appropriate time.
NS_INLINE CF_RETURNS_RETAINED CFTypeRef _Nullable CFBridgingRetain(id _Nullable X) {
    return (__bridge_retained CFTypeRef)X;
}

NS_INLINE id _Nullable CFBridgingRelease(CFTypeRef CF_CONSUMED _Nullable X) {
    return (__bridge_transfer id)X;
}

CFBridgingRetain 可以代替 **bridge_retained搜囱,CFBridgingRelease 可以代替 **bridge_transfer咐蚯。

RunLoop 初探

什么是 RunLoop

根據(jù)蘋(píng)果官方文檔的定義

Run loops are part of the fundamental infrastructure associated with threads.
RunLoop 是與線程相關(guān)的基礎(chǔ)結(jié)構(gòu)渠旁。

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.
RunLoop 是一個(gè)調(diào)度任務(wù)和處理事件的事件循環(huán)饵沧。其意義在于讓線程有事做的時(shí)候繁忙起來(lái)绪撵,沒(méi)事做的時(shí)候休眠瓢姻。

了解過(guò) NodeJS 事件循環(huán)機(jī)制的同學(xué)應(yīng)該不會(huì)對(duì) 事件循環(huán) 這一名詞陌生,用偽代碼實(shí)現(xiàn)如下:

    while(AppIsRunning) {
        id whoWakesMe = SleepForWakingUp();
        id event = GetEvent(whoWakesMe);
        HandleEvent(event);
    }

這份偽代碼來(lái)自 sunnyx 大神的 RunLoop 技術(shù)分享

可以看到音诈,RunLoop 就是這么一個(gè)簡(jiǎn)單的工作原理幻碱。但是其內(nèi)部實(shí)現(xiàn)要比上面的代碼復(fù)雜得多。我們下一節(jié)將深入 RunLoop 底層一探究竟细溅。

OSX/iOS 系統(tǒng)中褥傍,提供了兩個(gè)這樣的對(duì)象:NSRunLoop 和 CFRunLoopRef。

CFRunLoopRef 是在 CoreFoundation 框架內(nèi)的喇聊,它提供了純 C 函數(shù)的 API摔桦,所有這些 API 都是線程安全的。

NSRunLoop 是基于 CFRunLoopRef 的封裝承疲,提供了面向?qū)ο蟮?API邻耕,但是這些 API 不是線程安全的。

image.png

RunLoop 底層數(shù)據(jù)結(jié)構(gòu)

image.png

如上圖所示燕鸽,每一個(gè) RunLoop 內(nèi)部有多個(gè) mode兄世,而每一個(gè) mode 內(nèi)部有 sources0,source1, observers 和 timers啊研。mode 中的元素統(tǒng)稱為 mode item御滩。下面我們一起回顧下這五個(gè)類的數(shù)據(jù)結(jié)構(gòu)定義:

  • CFRunLoopRef
  • CFRunLoopModeRef
  • CFRunLoopSourceRef
  • CFRunLoopTimerRef
  • CFRunLoopObserverRef

CFRunLoopRef

typedef struct __CFRunLoop * CFRunLoopRef;

struct __CFRunLoop
{
    // CoreFoundation 中的 runtime 基礎(chǔ)信息
    CFRuntimeBase _base;
    // 針對(duì)獲取 mode 列表操作的鎖
    pthread_mutex_t _lock; /* locked for accessing mode list */
    // 喚醒端口
    __CFPort _wakeUpPort;  // used for CFRunLoopWakeUp
    // 是否使用過(guò)
    Boolean _unused;
    // runloop 運(yùn)行會(huì)重置的一個(gè)數(shù)據(jù)結(jié)構(gòu)
    volatile _per_run_data *_perRunData; // reset for runs of the run loop
    // runloop 所對(duì)應(yīng)線程
    pthread_t _pthread;
    uint32_t _winthread;
    // 存放 common mode 的集合
    CFMutableSetRef _commonModes;
    // 存放 common mode item 的集合
    CFMutableSetRef _commonModeItems;
    // runloop 當(dāng)前所在 mode
    CFRunLoopModeRef _currentMode;
    // 存放 mode 的集合
    CFMutableSetRef _modes;
    
    // runloop 內(nèi)部 block 鏈表表頭指針
    struct _block_item *_blocks_head;
    // runloop 內(nèi)部 block 鏈表表尾指針
    struct _block_item *_blocks_tail;
    // 運(yùn)行時(shí)間點(diǎn)
    CFAbsoluteTime _runTime;
    // 休眠時(shí)間點(diǎn)
    CFAbsoluteTime _sleepTime;
    CFTypeRef _counterpart;
};

// 每次 RunLoop 運(yùn)行后會(huì)重置
typedef struct _per_run_data
{
    uint32_t a;
    uint32_t b;
    uint32_t stopped;   // runloop 是否停止
    uint32_t ignoreWakeUps; // runloop 是否已喚醒
} _per_run_data;

// 鏈表節(jié)點(diǎn)
struct _block_item
{
    // 指向下一個(gè) _block_item
    struct _block_item *_next;
    // 要么是 string 類型,要么是集合類型党远,也就是說(shuō)一個(gè) block 可能對(duì)應(yīng)單個(gè)或多個(gè) mode
    CFTypeRef _mode; // CFString or CFSet
    // 存放的真正要執(zhí)行的 block
    void (^_block)(void);
};

通過(guò)上面的代碼可以看出削解,CFRunLoopRef 其實(shí)是一個(gè) __CFRunLoop 類型的結(jié)構(gòu)體指針。__CFRunLoop 內(nèi)部以下屬性需要重點(diǎn)關(guān)注:

  • _pthread
  • _commonModes
  • _currentMode
  • _modes

CFRunLoopModeRef

typedef struct __CFRunLoopMode *CFRunLoopModeRef;

struct __CFRunLoopMode
{
    // CoreFoundation 中的 runtime 基礎(chǔ)信息
    CFRuntimeBase _base;
    // 互斥鎖沟娱,加鎖前需要 runloop 先加鎖
    pthread_mutex_t _lock; /* must have the run loop locked before locking this */
    // mode 的名稱
    CFStringRef _name;
    // mode 是否停止
    Boolean _stopped;
    char _padding[3];
    // source0
    CFMutableSetRef _sources0;
    // source1
    CFMutableSetRef _sources1;
    // observers
    CFMutableArrayRef _observers;
    // timers
    CFMutableArrayRef _timers;
    CFMutableDictionaryRef _portToV1SourceMap;
    // port 的集合
    __CFPortSet _portSet;
    // observer 的 mask
    CFIndex _observerMask;
    // 如果定義了 GCD 定時(shí)器
#if USE_DISPATCH_SOURCE_FOR_TIMERS
    // GCD 定時(shí)器
    dispatch_source_t _timerSource;
    // 隊(duì)列
    dispatch_queue_t _queue;
    // 當(dāng) GCD 定時(shí)器觸發(fā)時(shí)設(shè)置為 true
    Boolean _timerFired; // set to true by the source when a timer has fired
    Boolean _dispatchTimerArmed;
#endif
// 如果使用 MK_TIMER
#if USE_MK_TIMER_TOO
    // MK_TIMER 的 port
    mach_port_t _timerPort;
    Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
    DWORD _msgQMask;
    void (*_msgPump)(void);
#endif
    // 定時(shí)器軟臨界點(diǎn)
    uint64_t _timerSoftDeadline; /* TSR */
    // 定時(shí)器硬臨界點(diǎn)
    uint64_t _timerHardDeadline; /* TSR */
};

通過(guò)上面的代碼可以看出氛驮,CFRunLoopModeRef 其實(shí)是一個(gè) __CFRunLoopMode 類型的結(jié)構(gòu)體指針。<br />__CFRunLoopMode 內(nèi)部以下屬性需要重點(diǎn)關(guān)注:

  • _sources0
  • _sources1
  • _observers
  • _timers

CFRunLoopSourceRef

typedef struct __CFRunLoopSource * CFRunLoopSourceRef;

struct __CFRunLoopSource
{
    // CoreFoundation 中的 runtime 基礎(chǔ)信息
    CFRuntimeBase _base;
    uint32_t _bits;
    // 互斥鎖
    pthread_mutex_t _lock;
    // source 的優(yōu)先級(jí)济似,值為小矫废,優(yōu)先級(jí)越高
    CFIndex _order; /* immutable */
    // runloop 集合
    CFMutableBagRef _runLoops;
    // 一個(gè)聯(lián)合體,說(shuō)明 source 要么為 source0砰蠢,要么為 source1
    union {
        CFRunLoopSourceContext version0;  /* immutable, except invalidation */
        CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
    } _context;
};

typedef struct {
    CFIndex version;
    // source 的信息
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
    // 判斷 source 相等的函數(shù)
    Boolean (*equal)(const void *info1, const void *info2);
    CFHashCode  (*hash)(const void *info);
    void    (*schedule)(void *info, CFRunLoopRef rl, CFStringRef mode);
    void    (*cancel)(void *info, CFRunLoopRef rl, CFStringRef mode);
    // source 要執(zhí)行的任務(wù)塊
    void    (*perform)(void *info);
} CFRunLoopSourceContext;

通過(guò)上面的代碼可以看出蓖扑,CFRunLoopSourceRef 其實(shí)是一個(gè) __CFRunLoopSource 類型的結(jié)構(gòu)體指針律杠。<br />__CFRunLoopSource 的結(jié)構(gòu)相對(duì)來(lái)說(shuō)要簡(jiǎn)單一點(diǎn),有幾個(gè)屬性要重點(diǎn)關(guān)注:

  • _order
  • _context
  • CFRunLoopSourceContext 下的 perform 函數(shù)指針

CFRunLoopTimerRef

typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;

struct __CFRunLoopTimer
{
    // CoreFoundation 中的 runtime 基礎(chǔ)信息
    CFRuntimeBase _base;
    uint16_t _bits;
    // 互斥鎖
    pthread_mutex_t _lock;
    // timer 對(duì)應(yīng)的 runloop
    CFRunLoopRef _runLoop;
    // timer 對(duì)應(yīng)的 mode 集合
    CFMutableSetRef _rlModes;
    // 下一次觸發(fā)時(shí)間點(diǎn)
    CFAbsoluteTime _nextFireDate;
    // 定時(shí)器每次任務(wù)的間隔
    CFTimeInterval _interval;        /* immutable */
    // 定時(shí)器所允許的誤差
    CFTimeInterval _tolerance;       /* mutable */
    // 觸發(fā)時(shí)間點(diǎn)
    uint64_t _fireTSR;               /* TSR units */
    // 定時(shí)器的優(yōu)先級(jí)
    CFIndex _order;                  /* immutable */
    // 定時(shí)器所要執(zhí)行的任務(wù)
    CFRunLoopTimerCallBack _callout; /* immutable */
    // 定時(shí)器上下文
    CFRunLoopTimerContext _context;  /* immutable, except invalidation */
};

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
} CFRunLoopTimerContext;

typedef void (*CFRunLoopTimerCallBack)(CFRunLoopTimerRef timer, void *info);

通過(guò)上面的代碼可以看出灰嫉,CFRunLoopTimerRef 其實(shí)是一個(gè) __CFRunLoopTimer 類型的結(jié)構(gòu)體指針诡蜓,并且這里 __CFRunLoopTimer 在前面還有一個(gè) CF_BRIDGED_MUTABLE_TYPE(NSTimer)

#define CF_BRIDGED_TYPE(T)      __attribute__((objc_bridge(T)))
#define CF_BRIDGED_MUTABLE_TYPE(T)  __attribute__((objc_bridge_mutable(T)))
#define CF_RELATED_TYPE(T,C,I)      __attribute__((objc_bridge_related(T,C,I)))
#else
#define CF_BRIDGED_TYPE(T)
#define CF_BRIDGED_MUTABLE_TYPE(T)
#define CF_RELATED_TYPE(T,C,I)
#endif

從這里說(shuō)明 CFRunLoopTimerRefNSTimertoll-free bridged 的胰挑。

__CFRunLoopTimer 中有幾個(gè)屬性需要重點(diǎn)關(guān)注:

  • _order
  • _callout
  • _context

CFRunLoopObserverRef

typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;

struct __CFRunLoopObserver
{
    // CoreFoundation 中的 runtime 基礎(chǔ)信息
    CFRuntimeBase _base;
    // 互斥鎖
    pthread_mutex_t _lock;
    // observer 對(duì)應(yīng)的 runloop
    CFRunLoopRef _runLoop;
    // observer 觀察了多少個(gè) runloop
    CFIndex _rlCount;
    CFOptionFlags _activities;          /* immutable */
    // observer 優(yōu)先級(jí)
    CFIndex _order;                     /* immutable */
    // observer 回調(diào)函數(shù)
    CFRunLoopObserverCallBack _callout; /* immutable */
    // observer 上下文
    CFRunLoopObserverContext _context;  /* immutable, except invalidation */
};

typedef struct {
    CFIndex version;
    void *  info;
    const void *(*retain)(const void *info);
    void    (*release)(const void *info);
    CFStringRef (*copyDescription)(const void *info);
} CFRunLoopObserverContext;

typedef void (*CFRunLoopObserverCallBack)(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info);

通過(guò)上面的代碼可以看出瞻颂,CFRunLoopObserverRef 其實(shí)是一個(gè) __CFRunLoopObserver 類型的結(jié)構(gòu)體指針贡这。

__CFRunLoopObserver 中有幾個(gè)屬性需要重點(diǎn)關(guān)注:

  • _order
  • _callout
  • _context

CFRunLoopActivity

/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
    kCFRunLoopEntry = (1UL << 0),
    kCFRunLoopBeforeTimers = (1UL << 1),
    kCFRunLoopBeforeSources = (1UL << 2),
    kCFRunLoopBeforeWaiting = (1UL << 5),
    kCFRunLoopAfterWaiting = (1UL << 6),
    kCFRunLoopExit = (1UL << 7),
    kCFRunLoopAllActivities = 0x0FFFFFFFU
};

通過(guò)上面的源碼可知盖矫,RunLoop 有以下?tīng)顟B(tài)

  • kCFRunLoopEntry: 即將進(jìn)入 Loop
  • kCFRunLoopBeforeTimers: 即將處理 Timer
  • kCFRunLoopBeforeSources: 即將處理 Source
  • kCFRunLoopBeforeWaiting: 即將進(jìn)入休眠
  • kCFRunLoopAfterWaiting: 剛從休眠中喚醒
  • kCFRunLoopExit: 即將推出 Loop

RunLoop 與線程關(guān)系

要想搞清楚 RunLoop 與線程的關(guān)系责掏,我們從 CFRunLoopGetMainCFRunLoopGetCurrent 函數(shù)開(kāi)始研究:

CFRunLoopRef CFRunLoopGetMain(void)
{
    CHECK_FOR_FORK();
    // 聲明一個(gè)空的 RunLoop 
    static CFRunLoopRef __main = NULL; // no retain needed
    // 如果 __main 為空换衬,則調(diào)用 _CFRunLoopGet0 函數(shù)來(lái)獲取 RunLoop
    if (!__main)
        __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
    return __main;
}

CFRunLoopRef CFRunLoopGetCurrent(void)
{
    CHECK_FOR_FORK();
    // 從 線程的 TSD(Thread Specific Data) 中獲取 runloop
    CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
    // 如果拿到了就返回
    if (rl)
        return rl;
    // 如果在 TSD 中沒(méi)有瞳浦,那么就調(diào)用 _CFRunLoopGet0 函數(shù)
    return _CFRunLoopGet0(pthread_self());
}

pthread_main_thread_np() 獲取的是主線程叫潦,其內(nèi)部實(shí)現(xiàn)如下

#define pthread_main_thread_np() _CF_pthread_main_thread_np()

CF_EXPORT pthread_t _CF_pthread_main_thread_np(void);
pthread_t _CF_pthread_main_thread_np(void) {
return _CFMainPThread;
}

void __CFInitialize(void) {
...

_CFMainPThread = pthread_self();

...
}

我們接著探索 _CFRunLoopGet0 函數(shù)诅挑,其實(shí)現(xiàn)如下

// 存儲(chǔ)了 runloop 與線程的字典
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;

// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
// t為0的話拔妥,表示是要獲取 主線程對(duì)應(yīng)的 RunLoop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t)
{
    // 如果傳入的現(xiàn)場(chǎng)為空,則再通過(guò) pthread_main_thread_np() 獲取到主線程
    if (pthread_equal(t, kNilPthreadT))
    {
        t = pthread_main_thread_np();
    }
    // 加鎖
    __CFLock(&loopsLock);
    // 如果 __CFRunLoops 字典為空达箍,則初始化字典没龙,并把 mainLoop 加入字典
    if (!__CFRunLoops)
    {
        // 解鎖
        __CFUnlock(&loopsLock);
        // 初始化一個(gè) 可變字典
        CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
        // 拿到主線程對(duì)應(yīng)的 RunLoop
        CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
        // 將 runloop 和 線程存入字典中,線程為 key硬纤,runloop 為 value
        CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
        if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void *volatile *)&__CFRunLoops))
        {
            // 釋放 字典
            CFRelease(dict);
        }
        // 釋放 mainLoop
        CFRelease(mainLoop);
        // 加鎖
        __CFLock(&loopsLock);
    }
    // 根據(jù)傳入的 線程 t 從字典中獲取 loop 對(duì)象
    CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
    // 對(duì) loops 字典解鎖
    __CFUnlock(&loopsLock);
    // 如果 loop 為空
    if (!loop)
    {
        // 創(chuàng)建一個(gè)新的 loop
        CFRunLoopRef newLoop = __CFRunLoopCreate(t);
        // 對(duì) loops 字典加鎖
        __CFLock(&loopsLock);
        // 從 loops 字典中獲取 loop
        loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
        if (!loop)
        {
            // 如果在字典中沒(méi)有找到 loop洼裤,把新的 loop 存入 loops 字典中
            CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
            loop = newLoop;
        }
        // don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
        // 不要在 字典鎖作用域里面釋放 runloop 對(duì)象,因?yàn)?CFRunLoopDeallocate 會(huì)釋放掉 runloop
        __CFUnlock(&loopsLock);
        // 釋放 newLoop
        CFRelease(newLoop);
    }
    // 判斷 t 是否是當(dāng)前線程
    if (pthread_equal(t, pthread_self()))
    {
        // 設(shè)置 thread specific data移国,簡(jiǎn)稱 TSD,這也是一個(gè)字典
        // 以 __CFTSDKeyRunLoop 為 key, loop 對(duì)象為 value 存入 TSD 中
        /**
         // Set some thread specific data in a pre-assigned slot. Don't pick a random value.
         Make sure you're using a slot that is unique.
         Pass in a destructor to free this data, or NULL if none is needed. Unlike pthread TSD, the destructor is per-thread.
         // 和 pthread 的 TSD 不一樣的時(shí)祝懂,第三個(gè)參數(shù) 析構(gòu)函數(shù)指針是每個(gè)線程都擁有的
         CF_EXPORT void *_CFSetTSD(uint32_t slot, void *newVal, void (*destructor)(void *));
         */
        _CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
        if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr))
        {
            _CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS - 1), (void (*)(void *))__CFFinalizeRunLoop);
        }
    }
    return loop;
}

通過(guò)上面的源碼,我們明確了在底層有一個(gè)字典的數(shù)據(jù)結(jié)構(gòu)。<br />這個(gè)字典存儲(chǔ)方式的是以 pthread_t 線程為 key缕允,CFRunLoopRef 為 value。<br />我們可以有以下的結(jié)論:

  • 每條線程都有唯一的一個(gè)與之對(duì)應(yīng)的RunLoop對(duì)象
  • RunLoop 保存在一個(gè)全局的 Dictionary 里驾霜,線程作為 key,RunLoop 作為 value
  • 線程剛創(chuàng)建時(shí)并沒(méi)有 RunLoop 對(duì)象蓉冈,RunLoop 會(huì)在第一次獲取它時(shí)創(chuàng)建
  • RunLoop 會(huì)在線程結(jié)束時(shí)銷毀
  • 主線程的 RunLoop 已經(jīng)自動(dòng)獲燃叶帷(創(chuàng)建)拉馋,子線程默認(rèn)沒(méi)有開(kāi)啟RunLoop

Runloop 底層

RunLoop 啟動(dòng)方式

void CFRunLoopRun(void)
{ /* DOES CALLOUT */
    int32_t result;
    do
    {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
{ /* DOES CALLOUT */
    CHECK_FOR_FORK();
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}

由上面的源碼可知:

  • 默認(rèn)啟動(dòng)方式,底層是通過(guò) CFRunLoopRun 來(lái)實(shí)現(xiàn)景馁,超時(shí)時(shí)間傳入的是 1.0e10绰精,這是一個(gè)很大的數(shù)字卿樱,所以可以理解為不超時(shí)繁调。
  • 自定義啟動(dòng)方式,可以配置超時(shí)時(shí)間以及 mode 等其它參數(shù)裕寨。

RunLoop 核心邏輯

我們接著進(jìn)入 CFRunLoopRunSpecific:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
{ /* DOES CALLOUT */
    CHECK_FOR_FORK();
    // 如果 runloop 正在回收中,直接返回 kCFRunLoopRunFinished 庆猫,表示 runloop 已經(jīng)完成
    if (__CFRunLoopIsDeallocating(rl))
        return kCFRunLoopRunFinished;
    // 對(duì) runloop 加鎖
    __CFRunLoopLock(rl);
    // 從 runloop 中查找給定的 mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
    // 如果找不到 mode好渠,且當(dāng)前 runloop 的 currentMode 也為空,進(jìn)入 if 邏輯
    // __CFRunLoopModeIsEmpty 函數(shù)結(jié)果為空的話霍掺,說(shuō)明 runloop 已經(jīng)處理完所有任務(wù)
    if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode))
    {
        Boolean did = false;
        // 如果 currentMode 不為空
        if (currentMode)
            // 對(duì) currentMode 解鎖
            __CFRunLoopModeUnlock(currentMode);
        // 對(duì) runloop 解鎖
        __CFRunLoopUnlock(rl);
        return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
    }
    // 暫時(shí)取出 runloop 的 per_run_data
    volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
    // 取出 runloop 的當(dāng)前 mode
    CFRunLoopModeRef previousMode = rl->_currentMode;
    // 將查找到的 mode 賦值到 runloop 的 _curentMode,也就是說(shuō)在這 runloop 完成了 mode 的切換
    rl->_currentMode = currentMode;
    // 初始化返回結(jié)果 result
    int32_t result = kCFRunLoopRunFinished;

    // 如果注冊(cè)了 observer 監(jiān)聽(tīng) kCFRunLoopEntry 狀態(tài)(即將進(jìn)入 loop)兔魂,則通知 observer
    if (currentMode->_observerMask & kCFRunLoopEntry)
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    // runloop 核心函數(shù) __CFRunLoopRun
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    // 如果注冊(cè)了 observer 監(jiān)聽(tīng) kCFRunLoopExit 狀態(tài)(即將推出 loop)长酗,則通知 observer
    if (currentMode->_observerMask & kCFRunLoopExit)
        __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
    // 對(duì) currentMode 解鎖
    __CFRunLoopModeUnlock(currentMode);
    // 還原原來(lái)的 previousPerRun
    __CFRunLoopPopPerRunData(rl, previousPerRun);
    // 還原原來(lái)的 mode
    rl->_currentMode = previousMode;
    // 對(duì) runloop 解鎖
    __CFRunLoopUnlock(rl);
    return result;
}

這塊的代碼精簡(jiǎn)一下之后:

SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled)
{ /* DOES CALLOUT */
    // 通知 Observers 進(jìn)入 loop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    
    // 具體要做的事情
    result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    
    // 通知 Observers 退出 loop
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    return result;
}

功夫不負(fù)有心人吊奢,終于來(lái)到了最核心的流程 __CFRunLoopRun 函數(shù)页滚,說(shuō)白了,一次運(yùn)行循環(huán)就是一次 __CFRunLoopRun 的運(yùn)行邦马。

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)

__CFRunLoopRun 參數(shù)有 5 個(gè),分別表示:

  • rl: CFRunLoopRef 對(duì)象
  • rlm: mode 的名稱
  • seconds: 超時(shí)時(shí)間
  • stopAfterHandle: 處理完 source 后是否直接返回
  • previousMode: 前一次運(yùn)行循環(huán)的 mode

下面提供 __CFRunLoopRun 的完整代碼和精簡(jiǎn)代碼随闽,讀者可根據(jù)自身需要選擇閱讀蛾扇。

__CFRunLoopRun 完整代碼

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)
{
    // 獲取開(kāi)始時(shí)間
    uint64_t startTSR = mach_absolute_time();

    if (__CFRunLoopIsStopped(rl))
    {
        // Runloop 處于停止?fàn)顟B(tài),修改runloop底層數(shù)據(jù)結(jié)構(gòu) 停止?fàn)顟B(tài) flag
        __CFRunLoopUnsetStopped(rl);
        // 返回 kCFRunLoopRunStopped 狀態(tài)
        return kCFRunLoopRunStopped;
    }
    else if (rlm->_stopped)
    {
        // Mode 處于停止?fàn)顟B(tài)更哄,修改 mode 的成員變量 _stopped 為 false
        rlm->_stopped = false;
        // 返回 kCFRunLoopRunStopped 狀態(tài)
        return kCFRunLoopRunStopped;
    }

    // 判斷是否有主隊(duì)列的 Mach Port
    mach_port_name_t dispatchPort = MACH_PORT_NULL;
    Boolean libdispatchQSafe = pthread_main_np() && ((HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && NULL == previousMode) || (!HANDLE_DISPATCH_ON_BASE_INVOCATION_ONLY && 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ)));
    if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name))
        dispatchPort = _dispatch_get_main_queue_port_4CF();

#if USE_DISPATCH_SOURCE_FOR_TIMERS
    // 從 mode 的 成員變量 _queue 中取出對(duì)應(yīng)的 Mach Port
    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

    // 聲明一個(gè)空的 GCD 定時(shí)器
    dispatch_source_t timeout_timer = NULL;
    // 初始化一個(gè) 「超時(shí)上下文」 結(jié)構(gòu)體指針對(duì)象
    struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
    // 如果 runloop 超時(shí)時(shí)間小于等于0
    if (seconds <= 0.0)
    { // instant timeout
        // 將 seconds 置為 0s
        seconds = 0.0;
        // 將 「超時(shí)上下文」的 termTSR 屬性設(shè)置為 0赦役,0ULL 后面的 ULL 表示 Unsigned Long Long 即無(wú)符號(hào) long long 整型
        timeout_context->termTSR = 0ULL;
    }
    else if (seconds <= TIMER_INTERVAL_LIMIT)
    {
        // 如果 runloop 超時(shí)時(shí)間小于等于 TIMER_INTERVAL_LIMIT 常量
        // 如果主線程存在术羔,則獲取主線程的主隊(duì)列
        // 如果主線程不存在聂示,則獲取全局隊(duì)列
        dispatch_queue_t queue = pthread_main_np() ? __CFDispatchQueueGetGenericMatchingMain() : __CFDispatchQueueGetGenericBackground();
        // 將隊(duì)列傳入并初始化一個(gè) GCD 對(duì)象秀鞭,對(duì)象類型為定時(shí)器
        timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
        // 對(duì) GCD 對(duì)象進(jìn)行 retain
        dispatch_retain(timeout_timer);
        // 將 GCD 對(duì)象賦值于 「超時(shí)上下文」 結(jié)構(gòu)體的 ds 屬性
        timeout_context->ds = timeout_timer;
        // 對(duì) runloop 對(duì)象進(jìn)行 retain皱坛,然后賦值于「超時(shí)上下文」 結(jié)構(gòu)體的 rl 屬性
        timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
        // 將本次運(yùn)行循環(huán)的開(kāi)始時(shí)間加上要運(yùn)行的時(shí)間得到runloop的生命周期時(shí)間
        // 然后賦值于「超時(shí)上下文」 結(jié)構(gòu)體的 termTSR 屬性
        timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
        
        // 設(shè)置 GCD 對(duì)象的上下文為 自定義的 「超時(shí)上下文」 結(jié)構(gòu)體
        dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
        
        // 設(shè)置 GCD 對(duì)象的回調(diào) 可以理解為要執(zhí)行的任務(wù)
        /**
         static void __CFRunLoopTimeout(void *arg)
         {
             struct __timeout_context *context = (struct __timeout_context *)arg;
             context->termTSR = 0ULL;
             CFRUNLOOP_WAKEUP_FOR_TIMEOUT();
             CFRunLoopWakeUp(context->rl);
             // The interval is DISPATCH_TIME_FOREVER, so this won't fire again
         }
         __CFRunLoopTimeout 函數(shù)內(nèi)部
         1.將 「超時(shí)上下文」 結(jié)構(gòu)體的 termTSR 屬性賦值為0
         2.CFRUNLOOP_WAKEUP_FOR_TIMEOUT() 宏內(nèi)部是一個(gè) do-while(0) 的無(wú)用操作,可以忽略
         3.然后通過(guò) CFRunLoopWakeUp 函數(shù)來(lái)喚醒 runloop
         */
        dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout);
        // 設(shè)置 GCD 對(duì)象的取消回調(diào) 可以理解為取消任務(wù)時(shí)的回調(diào)
        dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
        
        // 將開(kāi)始時(shí)間加上給定的 runloop 過(guò)期時(shí)間,因?yàn)榇藭r(shí)結(jié)果為 秒吭服,需要轉(zhuǎn)換為 納秒蝌戒,所以再乘以 10 ^ 9
        uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
        
        // 設(shè)置 GCD 定時(shí)器
        // 第二個(gè)參數(shù)表示定時(shí)器開(kāi)始時(shí)間 也就是說(shuō),如果是 CFRunLoopRun 進(jìn)來(lái)的話
        // 因?yàn)閭魅氲闹凳?1.0e10粹淋,所以這個(gè)定時(shí)器永遠(yuǎn)不會(huì)觸發(fā),也就意味著 runloop 永遠(yuǎn)不會(huì)超時(shí)
        // 第三個(gè)參數(shù)表示 定時(shí)器 納秒級(jí)的 間隔借杰,這里傳入的是 DISPATCH_TIME_FOREVER,說(shuō)明定時(shí)器只會(huì)觸發(fā)一次绞惦,不會(huì)觸發(fā)多次
        // 第四個(gè)參數(shù)表示 定時(shí)器 納秒級(jí) 的容錯(cuò)時(shí)間
        dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
        // 開(kāi)啟 GCD 定時(shí)器
        dispatch_resume(timeout_timer);
    }
    else
    { // infinite timeout
        // 設(shè)置超時(shí)時(shí)間為 9999999999
        // 然后設(shè)置 自定義的 「超時(shí)上下文」 結(jié)構(gòu)體的 termTSR 屬性值為 UNIT64_MAX
        seconds = 9999999999.0;
        timeout_context->termTSR = UINT64_MAX;
    }

    // 初始化一個(gè)布爾值 上一次是否通過(guò) dispatch port
    Boolean didDispatchPortLastTime = true;
    int32_t retVal = 0;
    do
    {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_state_t voucherState = VOUCHER_MACH_MSG_STATE_UNCHANGED;
        voucher_t voucherCopy = NULL;
#endif
        // 初始化一個(gè) 無(wú)符號(hào) int8 整型類型的 消息緩沖區(qū)菠发,緩沖區(qū)大小為 3KB
        uint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        // 初始化一個(gè)空的 mach_msg_header_t 類型的 msg 結(jié)構(gòu)體指針變量
        mach_msg_header_t *msg = NULL;
        // 初始化一個(gè)值為 MACH_PORT_NULL 的 port
        mach_port_t livePort = MACH_PORT_NULL;

        // 將 mode 的 _portSet 取出
        __CFPortSet waitSet = rlm->_portSet;

        // 設(shè)置 runloop 底層數(shù)據(jù)結(jié)構(gòu)的 ignoreWakeUps 值為 0
        __CFRunLoopUnsetIgnoreWakeUps(rl);

        // 判斷如果 mode 的 observer 的 mask 與上 kCFRunLoopBeforeTimers 枚舉值是否為1
        // 如果成功雁乡,通知 Observers 即將處理 Timers
        if (rlm->_observerMask & kCFRunLoopBeforeTimers)
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        // 判斷如果 mode 的 observer 的 mask 與上 kCFRunLoopBeforeSources 枚舉值是否為1
        // 如果成功踱稍,通知 Observers 即將處理 Sources
        if (rlm->_observerMask & kCFRunLoopBeforeSources)
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

        // 處理 Blocks,傳入 runloop 和 mode
        __CFRunLoopDoBlocks(rl, rlm);

        // 處理 Source0,傳入 runloop 和 mode侵浸,以及 stopAfterHandle
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        // 如果處理 Sources0 成功区端,再次處理 Blocks
        if (sourceHandledThisLoop)
        {
            __CFRunLoopDoBlocks(rl, rlm);
        }
        
        // 如果本次 loop 執(zhí)行 source0 成功 或 超時(shí)時(shí)間為0,設(shè)置 poll 為 true
        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        // 如果主隊(duì)列 port 不為空且上一次 loop 沒(méi)有主隊(duì)列 port
        if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime)
        {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
            // 初始化一個(gè) mach_msg_header_t 結(jié)構(gòu)的 msg
            msg = (mach_msg_header_t *)msg_buffer;
            // 判斷主隊(duì)列的port上是否有消息沥邻,如果有,進(jìn)入 handle_msg 流程
            if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL))
            {
                // 說(shuō)明主隊(duì)列的port上有消息過(guò)來(lái),需要喚醒 runloop
                goto handle_msg;
            }
#endif
        }
        
        // 將上一次 loop 主隊(duì)列 值為空
        didDispatchPortLastTime = false;

        // 說(shuō)明沒(méi)有從 port 拉取消息延届,并且 mode 的 _observerMask 與上 kCFRunLoopBeforeWaiting 為真,
        // 則通知 Observers 即將休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting))
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        
        
        /**
         CF_INLINE void __CFRunLoopSetSleeping(CFRunLoopRef rl)
         {
             __CFBitfieldSetValue(((CFRuntimeBase *)rl)->_cfinfo[CF_INFO_BITS], 1, 1, 1);
         }
         */
        // 設(shè)置 runloop sleeping 的flag
        __CFRunLoopSetSleeping(rl);
        // do not do any user callouts after this point (after notifying of sleeping)
        // 在 runloop 已經(jīng)睡眠之后赴涵,不要做任何用戶調(diào)用

        // 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.
        
        /**
         //必須在每個(gè)循環(huán)中都將 local-to-this-activation 端口推入
         //迭代扇苞,因?yàn)榇四J娇梢灾匦逻\(yùn)行鳖敷,我們不
         //希望這些端口可以被服務(wù)。
         */
        // 將主隊(duì)列 port 加入 waitSet 中
        __CFPortSetInsert(dispatchPort, waitSet);

        // 解鎖 mode
        __CFRunLoopModeUnlock(rlm);
        // 解鎖 runloop
        __CFRunLoopUnlock(rl);

        // 如果 poll 為真崖媚,sleepStart 為 0肴楷,否則為當(dāng)前時(shí)間
        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        // 這個(gè) do - while 循環(huán)主要針對(duì)于 GCD 定時(shí)器
        do
        {
            if (kCFUseCollectableAllocator)
            {
                // objc_clear_stack(0);
                // <rdar://problem/16393959>
                // 將 msg_buffer 緩沖區(qū) 置為0
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            msg = (mach_msg_header_t *)msg_buffer;
            // 判斷 waitSet 的 port 是否有消息
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

            // 判斷 modeQueuePort 這個(gè) port 不為空泥张,且 livePort 為 modeQueuePort
            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.
                /**
                 清空內(nèi)部隊(duì)列媚创。 如果其中一個(gè)標(biāo)注塊設(shè)置了 timerFired 標(biāo)志晌姚,請(qǐng)中斷并為計(jì)時(shí)器提供服務(wù)。
                 */
                while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue))
                    ;
                // 如果 mode 的 _timerFired 為 真宝磨,則置為 false,并退出 do-while 循環(huán)
                if (rlm->_timerFired)
                {
                    // Leave livePort as the queue port, and service timers below
                    rlm->_timerFired = false;
                    break;
                }
                else
                {
                    if (msg && msg != (mach_msg_header_t *)msg_buffer)
                        free(msg);
                }
            }
            else
            {
                // Go ahead and leave the inner loop.
                break;
            }
        } while (1);
#else
        if (kCFUseCollectableAllocator)
        {
            // objc_clear_stack(0);
            // <rdar://problem/16393959>
            // 將 msg_buffer 緩沖區(qū) 置為0
            memset(msg_buffer, 0, sizeof(msg_buffer));
        }
        msg = (mach_msg_header_t *)msg_buffer;
        // 判斷 waitSet 的 port 是否有消息
        __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
#endif

#endif
        // 對(duì) runloop 加鎖
        __CFRunLoopLock(rl);
        // 對(duì) mode 加鎖
        __CFRunLoopModeLock(rlm);

        // 如果 poll 為真,說(shuō)明主隊(duì)了喚醒了 runloop,反之則獲得 runloop 的睡眠時(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.
        
        
        /**
         
         CF_INLINE kern_return_t __CFPortSetRemove(__CFPort port, __CFPortSet portSet)
         {
             if (MACH_PORT_NULL == port)
             {
                 return -1;
             }
             return mach_port_extract_member(mach_task_self(), port, portSet);
         }
         
         */
        // 將 dispatchPort 從 waitSet 中移除,因?yàn)槊看?runloop 迭代中 dispatchPort 都會(huì)被加入 waitSet
        __CFPortSetRemove(dispatchPort, waitSet);
        
        /**
         CF_INLINE void __CFRunLoopSetIgnoreWakeUps(CFRunLoopRef rl)
         {
             rl->_perRunData->ignoreWakeUps = 0x57414B45; // 'WAKE'
         }
         喚醒 runloop
         */
        __CFRunLoopSetIgnoreWakeUps(rl);

        // user callouts now OK again
        // 設(shè)置 runloop 的 sleeping flag 為 0
        __CFRunLoopUnsetSleeping(rl);
        
        // 通知 Observers 結(jié)束休眠
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting))
            __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

    handle_msg:;
        /**
         CF_INLINE void __CFRunLoopSetIgnoreWakeUps(CFRunLoopRef rl)
         {
             rl->_perRunData->ignoreWakeUps = 0x57414B45; // 'WAKE'
         }
         表示收到了 port 消息靠胜,需要處理
         */
        __CFRunLoopSetIgnoreWakeUps(rl);

        // 如果喚醒的端口為空菠赚,啥事也不干
        if (MACH_PORT_NULL == livePort)
        {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
        }
        // 如果喚醒端口是 runloop 的 _wakeUpPort,則執(zhí)行 CFRUNLOOP_WAKEUP_FOR_WAKEUP() 函數(shù)
        // CFRUNLOOP_WAKEUP_FOR_WAKEUP() 函數(shù)其實(shí)啥也沒(méi)干
        else if (livePort == rl->_wakeUpPort)
        {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS
        }
#if USE_DISPATCH_SOURCE_FOR_TIMERS
        // 如果喚醒端口是 modeQueuePort,說(shuō)明是 定時(shí)器喚醒了 runloop
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort)
        {
            // 調(diào)用 CFRUNLOOP_WAKEUP_FOR_TIMER 函數(shù)塌忽,
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            // 執(zhí)行 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
        // 如果喚醒端口是 mode 的 _timerPort
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort)
        {
            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
            // 執(zhí)行 timer
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time()))
            {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
        // 如果喚醒端口是 dispatchPort嬉探,說(shuō)明是主隊(duì)列喚醒了 Runloop
        else if (livePort == dispatchPort)
        {
            // 執(zhí)行 CFRUNLOOP_WAKEUP_FOR_DISPATCH() 函數(shù)
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            // 解鎖 mode
            __CFRunLoopModeUnlock(rlm);
            // 解鎖 runloop
            __CFRunLoopUnlock(rl);
            // 設(shè)置 TSD眷蜓,以 __CFTSDKeyIsInGCDMainQ 為 key, 6 為值
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
            // 執(zhí)行主隊(duì)列任務(wù)
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            // 設(shè)置 TSD汽纤,以 __CFTSDKeyIsInGCDMainQ 為 key, 0 為值
            _CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
            // 對(duì) runloop 加鎖
            __CFRunLoopLock(rl);
            // 對(duì) mode 加鎖
            __CFRunLoopModeLock(rlm);
            // 標(biāo)記本次 loop 處理了主隊(duì)列
            sourceHandledThisLoop = true;
            didDispatchPortLastTime = true;
        }
        else
        {
            // 來(lái)到這,說(shuō)明是被 source1 喚醒
            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.
            voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);

            // Despite the name, this works for windows handles as well
            // 從端口中取出 source1
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
            if (rls)
            {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                mach_msg_header_t *reply = NULL;
                // 執(zhí)行 source1 z任務(wù)
                sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
                if (NULL != reply)
                {
                    (void)mach_msg(reply, MACH_SEND_MSG, reply->msgh_size, 0, MACH_PORT_NULL, 0, MACH_PORT_NULL);
                    CFAllocatorDeallocate(kCFAllocatorSystemDefault, reply);
                }
            }

            // 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
        // 處理 Blocks栋烤,傳入 runloop 和 mode
        __CFRunLoopDoBlocks(rl, rlm);

        /**
         CFRunLoopRunInMode() 函數(shù)返回的原因买窟,有四種
         // Reasons for CFRunLoopRunInMode() to Return
         enum {
             kCFRunLoopRunFinished = 1, // 完成了 loop
             kCFRunLoopRunStopped = 2,  // loop 被終止
             kCFRunLoopRunTimedOut = 3, // loop 超時(shí)
             kCFRunLoopRunHandledSource = 4 // loop 執(zhí)行了 主隊(duì)列任務(wù)
         };
         */
        // 如果執(zhí)行了主隊(duì)列任務(wù)并且 stopAfterHandle 為真瞳购,則退出 do-while 循環(huán)
        if (sourceHandledThisLoop && stopAfterHandle)
        {
            retVal = kCFRunLoopRunHandledSource;
        }
        // 如果超時(shí)亏推,則退出 do-while 循環(huán)
        else if (timeout_context->termTSR < mach_absolute_time())
        {
            retVal = kCFRunLoopRunTimedOut;
        }
        // 如果 runloop 已經(jīng)停止学赛,則退出 do-while 循環(huán)
        else if (__CFRunLoopIsStopped(rl))
        {
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        }
        // 如果 mode 的 _stopped 屬性為真,則退出 do-while 循環(huán)
        else if (rlm->_stopped)
        {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        }
        // 如果 mode 此時(shí)為空吞杭,則退出 do-while 循環(huán)
        else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode))
        {
            retVal = kCFRunLoopRunFinished;
        }

#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        voucher_mach_msg_revert(voucherState);
        os_release(voucherCopy);
#endif

    } while (0 == retVal);

    // 如果開(kāi)啟了 GCD 定時(shí)器
    if (timeout_timer)
    {
        // 停掉定時(shí)器并釋放資源
        dispatch_source_cancel(timeout_timer);
        dispatch_release(timeout_timer);
    }
    else
    {
        // 釋放 超時(shí)上下文 結(jié)構(gòu)體指針
        free(timeout_context);
    }

    return retVal;
}

__CFRunLoopRun 精簡(jiǎn)代碼

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode)
{
    int32_t retVal = 0;
    do
    {
        // 通知 Observers 即將處理 Timers
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        
        // 通知 Observers 即將處理 Sources
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

        // 處理 Blocks
        __CFRunLoopDoBlocks(rl, rlm);

        // 處理 Source0
        if (__CFRunLoopDoSources0(rl, rlm, stopAfterHandle))
        {
            // 處理 Blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }

        Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);

        // 判斷有無(wú) Source1
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL))
        {
            // 如果有 Source1,就跳轉(zhuǎn)到 handle_msg
            goto handle_msg;
        }

            
        didDispatchPortLastTime = false;

        // 通知 Observers 即將休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
            
        __CFRunLoopSetSleeping(rl);

        CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
        
        do
        {
            if (kCFUseCollectableAllocator)
            {
                // objc_clear_stack(0);
                // <rdar://problem/16393959>
                memset(msg_buffer, 0, sizeof(msg_buffer));
            }
            msg = (mach_msg_header_t *)msg_buffer;

            // 等待別的消息來(lái)喚醒當(dāng)前線程
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

            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.
                while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue))
                    ;
                if (rlm->_timerFired)
                {
                    // Leave livePort as the queue port, and service timers below
                    rlm->_timerFired = false;
                    break;
                }
                else
                {
                    if (msg && msg != (mach_msg_header_t *)msg_buffer)
                        free(msg);
                }
            }
            else
            {
                // Go ahead and leave the inner loop.
                break;
            }
        } while (1);

        // user callouts now OK again
        __CFRunLoopUnsetSleeping(rl);
        
        // 通知 Observers 結(jié)束休眠
        __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);

    handle_msg:
        if (被 timer 喚醒)
        {
            // 處理 timers
            __CFRunLoopDoTimers(rl, rlm, mach_absolute_time());
        }

        else if (被 GCD 喚醒)
        {
            // 處理 GCD 主隊(duì)列
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
        }
        else
        {
            // 被 Source1 喚醒
            __CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply) || sourceHandledThisLoop;
        }

        // 處理 Blocks
        __CFRunLoopDoBlocks(rl, rlm);

        if (sourceHandledThisLoop && stopAfterHandle)
        {
            retVal = kCFRunLoopRunHandledSource;
        }
        else if (timeout_context->termTSR < mach_absolute_time())
        {
            retVal = kCFRunLoopRunTimedOut;
        }
        else if (__CFRunLoopIsStopped(rl))
        {
            __CFRunLoopUnsetStopped(rl);
            retVal = kCFRunLoopRunStopped;
        }
        else if (rlm->_stopped)
        {
            rlm->_stopped = false;
            retVal = kCFRunLoopRunStopped;
        }
        else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode))
        {
            retVal = kCFRunLoopRunFinished;
        }

        voucher_mach_msg_revert(voucherState);
        os_release(voucherCopy);

    } while (0 == retVal);

    return retVal;
}

RunLoop 執(zhí)行的任務(wù)

通過(guò) __CFRunLoopRun芽狗,我們知道了 RunLoop 執(zhí)行的任務(wù)有以下幾個(gè):

  • __CFRunLoopDoObservers
  • __CFRunLoopDoBlocks
  • __CFRunLoopDoSources0
  • __CFRunLoopDoSource1
  • __CFRunLoopDoTimers
  • __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__

下面绢掰,我們將一個(gè)一個(gè)進(jìn)行分析:

__CFRunLoopDoObservers

/* rl is locked, rlm is locked on entrance and exit */
static void __CFRunLoopDoObservers() __attribute__((noinline));
static void __CFRunLoopDoObservers(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopActivity activity)
{ /* DOES CALLOUT */
    CHECK_FOR_FORK();

    // 判斷傳入的 mode 對(duì)應(yīng)的 observer 數(shù)量是否為 0
    CFIndex cnt = rlm->_observers ? CFArrayGetCount(rlm->_observers) : 0;
    if (cnt < 1)
        return;

    /* Fire the observers */
    
    // #define STACK_BUFFER_DECL(T, N, C) T N[C]
    // 這里可以分為兩種情況
    // 如果 mode 中的 observer 數(shù)量小于等于 1024 個(gè),那么相當(dāng)于 CFRunLoopObserverRef buffer[cnt]
    // 如果 mode 中的 observer 數(shù)量大于 1024 個(gè)童擎,那么相當(dāng)于 CFRunLoopObserverRef buffer[1]
    // 所以這里通過(guò) observer 數(shù)量來(lái)分配不同大小的緩存區(qū)
    STACK_BUFFER_DECL(CFRunLoopObserverRef, buffer, (cnt <= 1024) ? cnt : 1);
    
    
    // 判斷如果 observer 數(shù)量小于 1024 個(gè)滴劲,則把上一步得到的 buffer 緩存區(qū)賦值給 collectedObservers
    // 如果 observer 數(shù)量大于 1024 個(gè),則調(diào)用 malloc 函數(shù)來(lái)分配空間, 最終空間大小為 =  observer數(shù)目 * CFRunLoopObserverRef 類型占用內(nèi)存大小
    // 最終獲得了一個(gè) 類型為 CFRunLoopObserverRef 的數(shù)組指針變量 collectedObservers
    CFRunLoopObserverRef *collectedObservers = (cnt <= 1024) ? buffer : (CFRunLoopObserverRef *)malloc(cnt * sizeof(CFRunLoopObserverRef));
    
    CFIndex obs_cnt = 0;
    // 循環(huán)遍歷傳入的 runloop mode 中所有的 observers
    for (CFIndex idx = 0; idx < cnt; idx++)
    {
        // 從 observers 集合中取出一個(gè) observer
        CFRunLoopObserverRef rlo = (CFRunLoopObserverRef)CFArrayGetValueAtIndex(rlm->_observers, idx);
        
        // 滿足下列三個(gè)條件后進(jìn)入 if 內(nèi)部的邏輯
        // 1.判斷 observer 當(dāng)前的狀態(tài)是否與傳入的狀態(tài)匹配顾复,這里是通過(guò) 與 運(yùn)算來(lái)完成哑芹,如果相等,結(jié)果應(yīng)為 1捕透,所以這里判斷是不等于 0
        // 2.判斷 observer 是否是有效的
        /**
         // Bit 3 in the base reserved bits is used for invalid state in run loop objects

         CF_INLINE Boolean __CFIsValid(const void *cf)
         {
             return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3);
         }

         CF_INLINE void __CFSetValid(void *cf)
         {
             __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 1);
         }

         CF_INLINE void __CFUnsetValid(void *cf)
         {
             __CFBitfieldSetValue(((CFRuntimeBase *)cf)->_cfinfo[CF_INFO_BITS], 3, 3, 0);
         }
         
         */
        // 3.判斷 observer 是否已經(jīng) fire 了
        /**
         Bit 0 of the base reserved bits is used for firing state
         Bit 1 of the base reserved bits is used for repeats state
         CF_INLINE Boolean __CFRunLoopObserverIsFiring(CFRunLoopObserverRef rlo)
         {
             return (Boolean)__CFBitfieldGetValue(((const CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0);
         }
         */
        
        /**
         探索第二步和第三步操作之后聪姿,可以發(fā)現(xiàn) CFRuntimeBase 結(jié)構(gòu)中的
         Bit 0 是用來(lái)表示 啟動(dòng)狀態(tài)
         Bit 1 是用來(lái)表示 重復(fù)狀態(tài)
         Bit 3 是用來(lái)表示 runloop 對(duì)象的可用狀態(tài)
         */
        
        if (0 != (rlo->_activities & activity) && __CFIsValid(rlo) && !__CFRunLoopObserverIsFiring(rlo))
        {
            // 對(duì) observer 進(jìn)行 retain,然后存入 collectedObservers 集合中
            // CoreFoundation 框架中 需要手動(dòng)MRC乙嘀,也就是對(duì)接收到的對(duì)象需要調(diào)用 CFRetain末购,與此同時(shí),需要有配對(duì)的 CFRelease 操作防止內(nèi)存泄漏
            collectedObservers[obs_cnt++] = (CFRunLoopObserverRef)CFRetain(rlo);
        }
    }
    // 對(duì)傳入的 mode 解鎖
    // 底層實(shí)現(xiàn)是一個(gè)互斥鎖
    /**
     CF_INLINE void __CFRunLoopModeUnlock(CFRunLoopModeRef rlm)
     {
         //CFLog(6, CFSTR("__CFRunLoopModeLock unlocking %p"), rlm);
         pthread_mutex_unlock(&(rlm->_lock));
     }
     */
    __CFRunLoopModeUnlock(rlm);
    // 對(duì)傳入的 runloop 解鎖
    // 底層實(shí)現(xiàn)是一個(gè)互斥鎖
    /**
     CF_INLINE void __CFRunLoopUnlock(CFRunLoopRef rl)
     {
         //    CFLog(6, CFSTR("__CFRunLoopLock unlocking %p"), rl);
         pthread_mutex_unlock(&(((CFRunLoopRef)rl)->_lock));
     }
     */
    __CFRunLoopUnlock(rl);
    // 根據(jù)上面 for 循環(huán)內(nèi)部自增的變量 obs_cnt 虎谢,也就是有效的 observer 的個(gè)數(shù)進(jìn)行遍歷
    for (CFIndex idx = 0; idx < obs_cnt; idx++)
    {
        // 取出一個(gè) observer
        CFRunLoopObserverRef rlo = collectedObservers[idx];
        
        // 對(duì) observer 進(jìn)行加鎖
        // 底層實(shí)現(xiàn)是一把互斥鎖
        /**
         CF_INLINE void __CFRunLoopObserverLock(CFRunLoopObserverRef rlo)
         {
             pthread_mutex_lock(&(rlo->_lock));
             //    CFLog(6, CFSTR("__CFRunLoopObserverLock locked %p"), rlo);
         }
         */
        __CFRunLoopObserverLock(rlo);
        
        // 再次判斷 observer 是否已失效
        if (__CFIsValid(rlo))
        {
            // 從 observer 中取出 重復(fù)狀態(tài)盟榴,然后取反,賦值給 doInvalidate 布爾變量
            // 這個(gè)布爾變量在后面用作判斷婴噩,即如果 observer 的 repeat 為真擎场,那么 doInvalidate 就為假,
            // 如果 observer 的 repeat 為假几莽,那么 doInvalidate 就為真迅办,
            // 就會(huì)調(diào)用 CFRunLoopObserverInvalidate 來(lái)將 observer 從所屬 runloop 中剔除,然后 observer 被釋放
            Boolean doInvalidate = !__CFRunLoopObserverRepeats(rlo);
            
            // 設(shè)置 observer 的 runtimebase 里面的 bit 0 值為1 章蚣,即標(biāo)記 observer 已經(jīng)啟動(dòng)
            /**
             CF_INLINE void __CFRunLoopObserverSetFiring(CFRunLoopObserverRef rlo)
             {
                 __CFBitfieldSetValue(((CFRuntimeBase *)rlo)->_cfinfo[CF_INFO_BITS], 0, 0, 1);
             }
             */
            __CFRunLoopObserverSetFiring(rlo);
            
            // 對(duì) observer 解鎖
            __CFRunLoopObserverUnlock(rlo);
            
            // 調(diào)用 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ 函數(shù)站欺,傳入 observer 對(duì)應(yīng)的 callout,以及 observer 自己,runloop 當(dāng)前狀態(tài)矾策,以及 observer 上下文信息
            __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(rlo->_callout, rlo, activity, rlo->_context.info);
            
            if (doInvalidate)
            {
                CFRunLoopObserverInvalidate(rlo);
            }
            // 取消 observer 的 firing 狀態(tài)
            __CFRunLoopObserverUnsetFiring(rlo);
        }
        else
        {
            // 說(shuō)明 observer 已經(jīng)失效磷账,對(duì) observer 解鎖
            __CFRunLoopObserverUnlock(rlo);
        }
        // 因?yàn)榇藭r(shí) observer 要么已經(jīng)觸發(fā)了回調(diào),要么由于已經(jīng)失效啥也沒(méi)干贾虽,所以需要釋放釋放掉 observer逃糟,然后開(kāi)啟下一次循環(huán)過(guò)程
        CFRelease(rlo);
    }
    // 對(duì) runloop 加鎖
    __CFRunLoopLock(rl);
    // 對(duì) mode 加鎖
    __CFRunLoopModeLock(rlm);

    // 判斷集合如果不等于 buffer ,說(shuō)明是使用的 malloc 函數(shù)初始化而來(lái)蓬豁,需要調(diào)用 free 函數(shù)釋放
    if (collectedObservers != buffer)
        free(collectedObservers);
}

可以看到最終執(zhí)行回調(diào)任務(wù)的是 __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__绰咽,我們來(lái)到它的內(nèi)部:

static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(CFRunLoopObserverCallBack func, CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
    if (func)
    {
        func(observer, activity, info);
    }
    asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoBlocks

/**
 從 runloop 的 _block_item 鏈表里面匹配 mode,匹配上了的就執(zhí)行 block 任務(wù)
 */
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm)
{ // Call with rl and rlm locked
    /*
     因?yàn)?runloop 內(nèi)部有這樣的結(jié)構(gòu)
     
     struct _block_item *_blocks_head;  // 鏈表的頭指針
     struct _block_item *_blocks_tail;  // 鏈表的尾指針
     
     struct _block_item
     {
         struct _block_item *_next;     // 指向下一個(gè) _block_item
         CFTypeRef _mode; // CFString or CFSet // mode 可能是字符串庆尘,也可能是集合
         void (^_block)(void);          // block
     };
     
     所以 runloop 底層是以單鏈表的方式存儲(chǔ)著 _block_item 結(jié)構(gòu)體指針對(duì)象
     */
    // 如果 block 鏈表頭指針為空剃诅,說(shuō)明當(dāng)前 runloop 沒(méi)有要執(zhí)行的 block,直接返回 false
    if (!rl->_blocks_head)
        return false;
    // 如果 mode 為空或者 mode 的名稱為空驶忌,也直接返回 false
    if (!rlm || !rlm->_name)
        return false;

    // 是否執(zhí)行了 block
    Boolean did = false;
    // 取出 block 鏈表頭指針
    struct _block_item *head = rl->_blocks_head;
    // 取出 block 鏈表尾指針
    struct _block_item *tail = rl->_blocks_tail;
    
    // 將 runloop 上的 block 鏈表頭指針置空
    rl->_blocks_head = NULL;
    // 將 runloop 上的 block 鏈表尾指針置空
    rl->_blocks_tail = NULL;
    // 獲取 runloop 的 commonModes
    CFSetRef commonModes = rl->_commonModes;
    // 獲取傳入的 mode 的名稱
    CFStringRef curMode = rlm->_name;
    // 對(duì) mode 解鎖
    __CFRunLoopModeUnlock(rlm);
    // 對(duì) runloop 解鎖
    __CFRunLoopUnlock(rl);
    
    // 初始化一個(gè)空的指針
    struct _block_item *prev = NULL;
    // 初始化一個(gè)指向鏈表頭結(jié)點(diǎn)的指針
    struct _block_item *item = head;
    // 從取得的 block 鏈表頭指針開(kāi)始遍歷整個(gè) block 鏈表
    while (item)
    {
        // 當(dāng)前拿到的 _block_item 結(jié)構(gòu)體變量
        struct _block_item *curr = item;
        // 指針往前移動(dòng)一個(gè)位置矛辕,用于下一次循環(huán)
        item = item->_next;
        // 初始化 doit 布爾值
        Boolean doit = false;
        // 判斷 _block_item 的 mode 是字符串還是集合,即這個(gè) block_item 是對(duì)應(yīng)一個(gè) mode 還是多個(gè) mode
        if (CFStringGetTypeID() == CFGetTypeID(curr->_mode))
        {
            // 說(shuō)明是一個(gè) mode
            // 判斷兩種情況
            // 1.傳入的 mode 是否與 block_item 內(nèi)部的 mode 相等
            // 2.判斷 block_item 內(nèi)部的 mode 是否為 kCFRunLoopCommonModes付魔,同時(shí)判斷 commonModes 里面是否有傳入的 mode
            // 以上情況有一個(gè)滿足聊品,則 doit 為 true,否則 doit 為 false
            doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        }
        else
        {
            // 說(shuō)明是多個(gè) mode
            // 也是判斷兩種情況
            // 1.判斷 _block_item 內(nèi)部的 mode 集合是否包含傳入的 mode
            // 2.判斷 _block_item 內(nèi)部的 mode 集合是否包含 kCFRunLoopCommonModes几苍,以及 commonModes 里面是否有傳入的 mode
            // 以上情況有一個(gè)滿足翻屈,則 doit 為 true,否則 doit 為 false
            doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
        }
        // 說(shuō)白了只要 _block_item 節(jié)點(diǎn)滿足 mode 匹配關(guān)系妻坝,就要執(zhí)行其內(nèi)部的 block 任務(wù)
        
        // 如果 doit 為 false伸眶,說(shuō)明要執(zhí)行 block 任務(wù)的不是當(dāng)前的 _block_item,則將 prev 指針指向當(dāng)前的 _block_item
        // 然后因?yàn)樵谇懊嬉呀?jīng)做過(guò) item = item->_next 的操作了,此時(shí)的 item 已經(jīng)指向下一個(gè) _block_item 了
        if (!doit)
            prev = curr;
        
        // 如果 doit 為 true刽宪,說(shuō)明要執(zhí)行 block 任務(wù)的就是當(dāng)前的 _block_item
        if (doit)
        {
            // 這里其實(shí)可以分為三種情況
            // 一.目標(biāo)節(jié)點(diǎn)在頭尾之間的任一節(jié)點(diǎn)
            // 二.目標(biāo)節(jié)點(diǎn)在頭節(jié)點(diǎn)
            // 三.目標(biāo)節(jié)點(diǎn)在尾節(jié)點(diǎn)
            // 因?yàn)橐呀?jīng)執(zhí)行完了 block 任務(wù)厘贼,需要?jiǎng)h除目標(biāo)節(jié)點(diǎn)
            
            // 判斷 prev 指針是否為空,如果不為空圣拄,則將 prev 指針的 _next 指向 item嘴秸,注意,此時(shí) item 為下一次要遍歷的 _block_item
            // 這里的作用就是情況一
            if (prev)
                prev->_next = item;
            
            // 下面分別判斷了兩種邊界值情況庇谆,即要執(zhí)行 block 的任務(wù)為頭節(jié)點(diǎn)或尾節(jié)點(diǎn)
            
            // 判斷如果當(dāng)前正在遍歷的 _block_time 是否等于 head 指針
            // 因?yàn)?head 在進(jìn)入 while 循環(huán)之前指向的鏈表的頭結(jié)點(diǎn)岳掐,所以能夠進(jìn)入 if 內(nèi)部邏輯的條件
            // 說(shuō)白了也就是剛好頭結(jié)點(diǎn)就是要執(zhí)行 block 任務(wù)的那個(gè)節(jié)點(diǎn),此時(shí)將 head 指針指向下一節(jié)點(diǎn)饭耳,在循環(huán)結(jié)束后有一個(gè)判斷串述,
            // 如果 head 不為空,然后里面的有一步操作就是重新讓 rl->_blocks_head 指向 head哥攘,也就是說(shuō) 執(zhí)行了 block 任務(wù)且剛好為頭節(jié)點(diǎn)的節(jié)點(diǎn)被刪除了剖煌。
            
            // 這里的作用就是情況二
            if (curr == head)
                head = item;
            
            // 判斷如果當(dāng)前正在遍歷的 _block_time 是否等于 tail 指針
            // 因?yàn)?tail 指針在 while 循環(huán)之前是指向的鏈表的尾節(jié)點(diǎn)材鹦,所有能夠進(jìn)入 if 內(nèi)部邏輯條件
            // 說(shuō)白了就是剛好尾節(jié)點(diǎn)就是要執(zhí)行 block 任務(wù)的那個(gè)節(jié)點(diǎn)逝淹,此時(shí)將 tail 指針指向 prev 指針指向的節(jié)點(diǎn)耕姊,
            // 而 prev 指針的指向,我們知道栅葡,顯然就是尾節(jié)點(diǎn)的前一個(gè)節(jié)點(diǎn)茉兰,說(shuō)白了就是把執(zhí)行了 block 任務(wù)且剛好為尾節(jié)點(diǎn)的節(jié)點(diǎn)被刪除了
            
            // 這里的作用就是情況三
            if (curr == tail)
                tail = prev;
            
            // 取出如果當(dāng)前正在遍歷的 _block_item 中的 block
            void (^block)(void) = curr->_block;
            // 釋放如果當(dāng)前正在遍歷的 _block_item 的 mode
            CFRelease(curr->_mode);
            // 釋放如果當(dāng)前正在遍歷的 _block_item
            free(curr);
            
            // 再次判斷 doit
            if (doit)
            {
                // 傳入 block,執(zhí)行任務(wù)
                __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
                // 將 did 置為 true
                did = true;
            }
            // 釋放 block
            Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
            // 在重新加鎖之前釋放 blcok 可以防止程序員在 dealloc 方法里面重新讓 runloop 運(yùn)行起來(lái)而導(dǎo)致死鎖的情況
        }
    }
    // 對(duì) runloop 加鎖
    __CFRunLoopLock(rl);
    // 對(duì)  mode 加鎖
    __CFRunLoopModeLock(rlm);
    // 如果 head 指針不為空欣簇,這里 if 的邏輯也是必進(jìn)
    if (head)
    {
        // 因?yàn)樵谇懊嬉呀?jīng)對(duì) rl->_blocks_head 指針進(jìn)行了置空操作规脸,這里等價(jià)于 tail->_next = NULL
        tail->_next = rl->_blocks_head;
        // 讓 rl->_blocks_head 指向 head 指針指向的節(jié)點(diǎn)
        // 如果在上面的 while 循環(huán)中,沒(méi)找到要執(zhí)行的 block熊咽,那么 head 其實(shí)就是原來(lái) rl->_blocks_head 的值
        // 如果在上面的 while 循環(huán)中莫鸭,找到了要執(zhí)行的 block,那么 head 是指向的
        rl->_blocks_head = head;
        // 因?yàn)樵谇懊嬉呀?jīng)對(duì) rl->_blocks_tail 指針進(jìn)行了置空操作,所以這里 if 的邏輯必進(jìn)
        // 所以這里就是讓 rl->_blocks_tail 指向 tail 指針指向的節(jié)點(diǎn)
        if (!rl->_blocks_tail)
            rl->_blocks_tail = tail;
    }
    return did;
}

可以看到真正執(zhí)行回調(diào)函數(shù)的是 CFRUNLOOP_IS_CALLING_OUT_TO_A__BLOCK 函數(shù)横殴,其內(nèi)部實(shí)現(xiàn)如下

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(void (^block)(void))
{
    if (block)
    {
        block();
    }
    asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoSources0

/* rl is locked, rlm is locked on entrance and exit */
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle) __attribute__((noinline));
static Boolean __CFRunLoopDoSources0(CFRunLoopRef rl, CFRunLoopModeRef rlm, Boolean stopAfterHandle)
{ /* DOES CALLOUT */
    CHECK_FOR_FORK();
    // 初始化 sources 被因,并置空
    CFTypeRef sources = NULL;
    // 初始化返回值 sourceHandled,并置為 false 衫仑,意為是否已經(jīng)處理了 source0
    Boolean sourceHandled = false;

    /* Fire the version 0 sources */
    if (NULL != rlm->_sources0 && 0 < CFSetGetCount(rlm->_sources0))
    {
        // 判斷 mode 的 _source0 不為空且大小大于0
        // CFSetApplyFunction (Calls a function once for each value in the set.)
        // 這個(gè)函數(shù)的作用是對(duì)傳入的 set 里面的每個(gè)元素執(zhí)行 傳入的函數(shù)指針
        // 第一個(gè)參數(shù)是 set
        // 第二個(gè)參數(shù)是 要對(duì)set每個(gè)元素執(zhí)行一次的函數(shù)指針
        // 第三個(gè)參數(shù)作為傳入的函數(shù)指針的第二個(gè)參數(shù)
        CFSetApplyFunction(rlm->_sources0, (__CFRunLoopCollectSources0), &sources);
    }
    // 此時(shí) sources 里面已經(jīng)有全部的 source0 了
    if (NULL != sources)
    {
        // 對(duì) mode 解鎖
        __CFRunLoopModeUnlock(rlm);
        // 對(duì) runloop 解鎖
        __CFRunLoopUnlock(rl);
        // sources 有可能是一個(gè) runloop source 也有可能是一個(gè)裝的 runloop source 的數(shù)組
        // sources is either a single (retained) CFRunLoopSourceRef or an array of (retained) CFRunLoopSourceRef
        if (CFGetTypeID(sources) == CFRunLoopSourceGetTypeID())
        {
            // sources 是單個(gè) runloop source
            // 類型強(qiáng)轉(zhuǎn)一下
            CFRunLoopSourceRef rls = (CFRunLoopSourceRef)sources;
            // 對(duì) rls 加鎖
            __CFRunLoopSourceLock(rls);
            if (__CFRunLoopSourceIsSignaled(rls))
            {
                // 來(lái)到這梨与,說(shuō)明 rls 被標(biāo)記為 signaled
                // 取消 signaled 的標(biāo)志
                __CFRunLoopSourceUnsetSignaled(rls);
                // 判斷 rls 是否有效
                if (__CFIsValid(rls))
                {
                    // 對(duì) rls 進(jìn)行解鎖
                    __CFRunLoopSourceUnlock(rls);
                    // 執(zhí)行 source0 回調(diào)
                    /**
                     static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));
                     static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info)
                     {
                         if (perform)
                         {
                             perform(info);
                         }
                         asm __volatile__(""); // thwart tail-call optimization
                     }
                     */
                    __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
                    CHECK_FOR_FORK();
                    // 將處理 source0 的結(jié)果置為 true
                    sourceHandled = true;
                }
                else
                {
                    __CFRunLoopSourceUnlock(rls);
                }
            }
            else
            {
                // 對(duì) rls 解鎖,來(lái)到這文狱,說(shuō)明 rls 沒(méi)有被標(biāo)記為 signaled
                __CFRunLoopSourceUnlock(rls);
            }
        }
        else
        {
            // sources 是數(shù)組
            // 取出 source0 的個(gè)數(shù)
            CFIndex cnt = CFArrayGetCount((CFArrayRef)sources);
            // 對(duì) sources 進(jìn)行排序粥鞋,按照 _order 進(jìn)行升序排序
            /**
             static CFComparisonResult __CFRunLoopSourceComparator(const void *val1, const void *val2, void *context)
             {
                 CFRunLoopSourceRef o1 = (CFRunLoopSourceRef)val1;
                 CFRunLoopSourceRef o2 = (CFRunLoopSourceRef)val2;
                 if (o1->_order < o2->_order)
                     return kCFCompareLessThan;
                 if (o2->_order < o1->_order)
                     return kCFCompareGreaterThan;
                 return kCFCompareEqualTo;
             }
             */
            CFArraySortValues((CFMutableArrayRef)sources, CFRangeMake(0, cnt), (__CFRunLoopSourceComparator), NULL);
            // 遍歷 sources 數(shù)組
            for (CFIndex idx = 0; idx < cnt; idx++)
            {
                // 取出一個(gè) source0 rls
                CFRunLoopSourceRef rls = (CFRunLoopSourceRef)CFArrayGetValueAtIndex((CFArrayRef)sources, idx);
                // 對(duì) rls 加鎖
                __CFRunLoopSourceLock(rls);
                if (__CFRunLoopSourceIsSignaled(rls))
                {
                    // 來(lái)到這,說(shuō)明 rls 被標(biāo)記為 signaled
                    // 取消 signaled 的標(biāo)志
                    __CFRunLoopSourceUnsetSignaled(rls);
                    // 判斷 rls 是否有效
                    if (__CFIsValid(rls))
                    {
                        // 對(duì) rls 進(jìn)行解鎖
                        __CFRunLoopSourceUnlock(rls);
                        // 執(zhí)行 source0 回調(diào)
                        /**
                         static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));
                         static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info)
                         {
                             if (perform)
                             {
                                 perform(info);
                             }
                             asm __volatile__(""); // thwart tail-call optimization
                         }
                         */
                        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(rls->_context.version0.perform, rls->_context.version0.info);
                        CHECK_FOR_FORK();
                        // 將處理 source0 的結(jié)果置為 true
                        sourceHandled = true;
                    }
                    else
                    {
                        __CFRunLoopSourceUnlock(rls);
                    }
                }
                else
                {
                    // 對(duì) rls 解鎖瞄崇,來(lái)到這呻粹,說(shuō)明 rls 沒(méi)有被標(biāo)記為 signaled
                    __CFRunLoopSourceUnlock(rls);
                }
                // 如果有一個(gè) source0 處理完成且 傳入的 stopAfterHandle 為 true苏研,則跳出循環(huán)
                if (stopAfterHandle && sourceHandled)
                {
                    break;
                }
            }
        }
        // 釋放 sources
        CFRelease(sources);
        // 對(duì) runloop 加鎖
        __CFRunLoopLock(rl);
        // 對(duì) mode 解鎖
        __CFRunLoopModeLock(rlm);
    }
    // 返回處理 source0 結(jié)果
    return sourceHandled;
}

而真正執(zhí)行回調(diào)的是 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ 函數(shù),其內(nèi)部實(shí)現(xiàn)如下

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(void (*perform)(void *), void *info)
{
    if (perform)
    {
        perform(info);
    }
    asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoSource1

// msg, size and reply are unused on Windows
static Boolean __CFRunLoopDoSource1() __attribute__((noinline));
static Boolean __CFRunLoopDoSource1(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopSourceRef rls
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                                    ,
                                    mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply
#endif
)
{ /* DOES CALLOUT */
    CHECK_FOR_FORK();
    // 初始化 是否處理成功 source1 結(jié)果
    Boolean sourceHandled = false;

    /* Fire a version 1 source */
    // 對(duì) source1 進(jìn)行 retian
    CFRetain(rls);
    // 對(duì) mode 解鎖
    __CFRunLoopModeUnlock(rlm);
    // 對(duì) runloop 解鎖
    __CFRunLoopUnlock(rl);
    // 對(duì) source1 加鎖
    __CFRunLoopSourceLock(rls);
    // 如果 source1 有效
    if (__CFIsValid(rls))
    {
        /**
         // Bit 1 of the base reserved bits is used for signalled state

         CF_INLINE Boolean __CFRunLoopSourceIsSignaled(CFRunLoopSourceRef rls)
         {
             return (Boolean)__CFBitfieldGetValue(rls->_bits, 1, 1);
         }

         CF_INLINE void __CFRunLoopSourceSetSignaled(CFRunLoopSourceRef rls)
         {
             __CFBitfieldSetValue(rls->_bits, 1, 1, 1);
         }

         CF_INLINE void __CFRunLoopSourceUnsetSignaled(CFRunLoopSourceRef rls)
         {
             __CFBitfieldSetValue(rls->_bits, 1, 1, 0);
         }
         */
        // 設(shè)置 source1 狀態(tài)為未激活
        __CFRunLoopSourceUnsetSignaled(rls);
        // 解鎖 source1
        __CFRunLoopSourceUnlock(rls);
        __CFRunLoopDebugInfoForRunLoopSource(rls);
        // 執(zhí)行 source1 回調(diào)
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(rls->_context.version1.perform,
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
                                                                   msg, size, reply,
#endif
                                                                   rls->_context.version1.info);
        CHECK_FOR_FORK();
        sourceHandled = true;
    }
    else
    {
        // 寫(xiě)入到日志驱显,source1 失效
        if (_LogCFRunLoop)
        {
            CFLog(kCFLogLevelDebug, CFSTR("%p (%s) __CFRunLoopDoSource1 rls %p is invalid"), CFRunLoopGetCurrent(), *_CFGetProgname(), rls);
        }
        // 對(duì) source1 解鎖
        __CFRunLoopSourceUnlock(rls);
    }
    // 釋放 source1
    CFRelease(rls);
    // 對(duì) runloop 加鎖
    __CFRunLoopLock(rl);
    // 對(duì) mode 加鎖
    __CFRunLoopModeLock(rlm);
    return sourceHandled;
}

其中真正的回調(diào)函數(shù)為 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__ 鹰霍,其內(nèi)部實(shí)現(xiàn)為:


static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
    void *(*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info),
    mach_msg_header_t *msg, CFIndex size, mach_msg_header_t **reply,
#else
    void (*perform)(void *),
#endif
    void *info)
{
    if (perform)
    {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
        *reply = perform(msg, size, kCFAllocatorSystemDefault, info);
#else
        perform(info);
#endif
    }
    asm __volatile__(""); // thwart tail-call optimization
}

__CFRunLoopDoTimers

// rl and rlm are locked on entry and exit
static Boolean __CFRunLoopDoTimers(CFRunLoopRef rl, CFRunLoopModeRef rlm, uint64_t limitTSR)
{ /* DOES CALLOUT */
    // 初始化 timer 是否被處理成功 結(jié)果
    Boolean timerHandled = false;
    // 初始化一個(gè)空的 timers 集合
    CFMutableArrayRef timers = NULL;
    // 遍歷 mode 的 _itemrs 定時(shí)器數(shù)組
    for (CFIndex idx = 0, cnt = rlm->_timers ? CFArrayGetCount(rlm->_timers) : 0; idx < cnt; idx++)
    {
        // 取出 timer
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(rlm->_timers, idx);
        
        // 如果 timer 有效且尚未被觸發(fā)
        if (__CFIsValid(rlt) && !__CFRunLoopTimerIsFiring(rlt))
        {
            // 并且 timer 的觸發(fā)時(shí)間小于等于 限定的時(shí)間
            if (rlt->_fireTSR <= limitTSR)
            {
                // 初始化 timers 集合
                if (!timers)
                    timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeArrayCallBacks);
                // 把 timer 加入 timers 集合
                CFArrayAppendValue(timers, rlt);
            }
        }
    }

    // 遍歷 timers 集合送巡,里面裝的都是要干活的 timer
    for (CFIndex idx = 0, cnt = timers ? CFArrayGetCount(timers) : 0; idx < cnt; idx++)
    {
        // 取出 timer
        CFRunLoopTimerRef rlt = (CFRunLoopTimerRef)CFArrayGetValueAtIndex(timers, idx);
        // 執(zhí)行 timer 回調(diào)
        Boolean did = __CFRunLoopDoTimer(rl, rlm, rlt);
        timerHandled = timerHandled || did;
    }
    // 釋放 timers 集合
    if (timers)
        CFRelease(timers);
    return timerHandled;
}

static Boolean __CFRunLoopDoTimer(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFRunLoopTimerRef rlt)
{ /* DOES CALLOUT */
    // 初始化 timer 被處理成功結(jié)果
    Boolean timerHandled = false;
    uint64_t oldFireTSR = 0;

    /* Fire a timer */
    // 對(duì) timer retain
    CFRetain(rlt);
    // 對(duì) timer 加鎖
    __CFRunLoopTimerLock(rlt);

    // 如果 timer 有效,并且 timer 觸發(fā)時(shí)間點(diǎn)未到骗爆,以及 timer 沒(méi)有被觸發(fā)次氨,以及 timer 的 runloop 就是傳入的 runloop ,進(jìn)入 if 邏輯
    if (__CFIsValid(rlt) && rlt->_fireTSR <= mach_absolute_time() && !__CFRunLoopTimerIsFiring(rlt) && rlt->_runLoop == rl)
    {
        // 初始化空的 context_info
        void *context_info = NULL;
        // 初始化空的 context_release 函數(shù)指針
        void (*context_release)(const void *) = NULL;
        // 如果 timer 的 context 屬性的 retain 函數(shù)指針不為空摘投,進(jìn)入 if 邏輯
        if (rlt->_context.retain)
        {
            // 將 info retain 后賦值給 context_info
            context_info = (void *)rlt->_context.retain(rlt->_context.info);
            // 取出 timer 的 release 函數(shù)指針賦值給 context_release
            context_release = rlt->_context.release;
        }
        else
        {
            // 取出 timer 的 info
            context_info = rlt->_context.info;
        }
        // timer 的時(shí)間間隔是否為 0
        Boolean doInvalidate = (0.0 == rlt->_interval);
        // 設(shè)置 timer 的 是否觸發(fā)的 bits 為1
        __CFRunLoopTimerSetFiring(rlt);
        // Just in case the next timer has exactly the same deadlines as this one, we reset these values so that the arm next timer code can correctly find the next timer in the list and arm the underlying timer.
        // 以防萬(wàn)一下一個(gè)計(jì)時(shí)器與該計(jì)時(shí)器的截止日期完全相同煮寡,我們重置這些值虹蓄,以便啟用下一個(gè)計(jì)時(shí)器代碼可以正確地找到列表中的下一個(gè)計(jì)時(shí)器并啟用基礎(chǔ)計(jì)時(shí)器。
        rlm->_timerSoftDeadline = UINT64_MAX;
        rlm->_timerHardDeadline = UINT64_MAX;
        // 解鎖 timer
        __CFRunLoopTimerUnlock(rlt);
        // 對(duì) timer 觸發(fā)加鎖
        __CFRunLoopTimerFireTSRLock();
        // 取出 _fireTSR
        oldFireTSR = rlt->_fireTSR;
        // 對(duì) timer 觸發(fā)解鎖
        __CFRunLoopTimerFireTSRUnlock();
        
        // 裝配下一個(gè) timer
        __CFArmNextTimerInMode(rlm, rl);

        // 對(duì) mode 解鎖
        __CFRunLoopModeUnlock(rlm);
        // 對(duì) runloop 解鎖
        __CFRunLoopUnlock(rl);
        // 執(zhí)行 timer 真正的回調(diào)
        __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(rlt->_callout, rlt, context_info);
        CHECK_FOR_FORK();
        // 如果 timer 只跑一次
        if (doInvalidate)
        {
            // 讓 timer 失效
            CFRunLoopTimerInvalidate(rlt); /* DOES CALLOUT */
        }
        // 如果 context_release 函數(shù)指針不為空幸撕,則釋放掉 context_info
        if (context_release)
        {
            context_release(context_info);
        }
        // 對(duì) runloop 加鎖
        __CFRunLoopLock(rl);
        // 對(duì) mode 解鎖
        __CFRunLoopModeLock(rlm);
        // 對(duì) timer 加鎖
        __CFRunLoopTimerLock(rlt);
        // 設(shè)置 處理 timer 結(jié)果為 true
        timerHandled = true;
        // 設(shè)置 timer 的 是否觸發(fā)的 bits 為0
        __CFRunLoopTimerUnsetFiring(rlt);
    }
    // 如果 timer 有效薇组,并且 timer 被處理成功
    if (__CFIsValid(rlt) && timerHandled)
    {
        /* This is just a little bit tricky: we want to support calling
         * CFRunLoopTimerSetNextFireDate() from within the callout and
         * honor that new time here if it is a later date, otherwise
         * it is completely ignored. */
        // 如果 舊的觸發(fā)時(shí)間 小于 當(dāng)前的觸發(fā)時(shí)間
        if (oldFireTSR < rlt->_fireTSR)
        {
            /* Next fire TSR was set, and set to a date after the previous
            * fire date, so we honor it. */
            __CFRunLoopTimerUnlock(rlt);
            // The timer was adjusted and repositioned, during the
            // callout, but if it was still the min timer, it was
            // skipped because it was firing.  Need to redo the
            // min timer calculation in case rlt should now be that
            // timer instead of whatever was chosen.
            __CFArmNextTimerInMode(rlm, rl);
        }
        else
        {
            uint64_t nextFireTSR = 0LL;
            uint64_t intervalTSR = 0LL;
            if (rlt->_interval <= 0.0)
            {
            }
            else if (TIMER_INTERVAL_LIMIT < rlt->_interval)
            {
                intervalTSR = __CFTimeIntervalToTSR(TIMER_INTERVAL_LIMIT);
            }
            else
            {
                intervalTSR = __CFTimeIntervalToTSR(rlt->_interval);
            }
            if (LLONG_MAX - intervalTSR <= oldFireTSR)
            {
                nextFireTSR = LLONG_MAX;
            }
            else
            {
                if (intervalTSR == 0)
                {
                    // 15304159: Make sure we don't accidentally loop forever here
                    CRSetCrashLogMessage("A CFRunLoopTimer with an interval of 0 is set to repeat");
                    HALT;
                }
                uint64_t currentTSR = mach_absolute_time();
                nextFireTSR = oldFireTSR;
                while (nextFireTSR <= currentTSR)
                {
                    nextFireTSR += intervalTSR;
                }
            }
            CFRunLoopRef rlt_rl = rlt->_runLoop;
            if (rlt_rl)
            {
                CFRetain(rlt_rl);
                CFIndex cnt = CFSetGetCount(rlt->_rlModes);
                STACK_BUFFER_DECL(CFTypeRef, modes, cnt);
                CFSetGetValues(rlt->_rlModes, (const void **)modes);
                // To avoid A->B, B->A lock ordering issues when coming up
                // towards the run loop from a source, the timer has to be
                // unlocked, which means we have to protect from object
                // invalidation, although that's somewhat expensive.
                for (CFIndex idx = 0; idx < cnt; idx++)
                {
                    CFRetain(modes[idx]);
                }
                __CFRunLoopTimerUnlock(rlt);
                for (CFIndex idx = 0; idx < cnt; idx++)
                {
                    CFStringRef name = (CFStringRef)modes[idx];
                    modes[idx] = (CFTypeRef)__CFRunLoopFindMode(rlt_rl, name, false);
                    CFRelease(name);
                }
                __CFRunLoopTimerFireTSRLock();
                rlt->_fireTSR = nextFireTSR;
                rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
                for (CFIndex idx = 0; idx < cnt; idx++)
                {
                    CFRunLoopModeRef rlm = (CFRunLoopModeRef)modes[idx];
                    if (rlm)
                    {
                        __CFRepositionTimerInMode(rlm, rlt, true);
                    }
                }
                __CFRunLoopTimerFireTSRUnlock();
                for (CFIndex idx = 0; idx < cnt; idx++)
                {
                    __CFRunLoopModeUnlock((CFRunLoopModeRef)modes[idx]);
                }
                CFRelease(rlt_rl);
            }
            else
            {
                __CFRunLoopTimerUnlock(rlt);
                __CFRunLoopTimerFireTSRLock();
                rlt->_fireTSR = nextFireTSR;
                rlt->_nextFireDate = CFAbsoluteTimeGetCurrent() + __CFTimeIntervalUntilTSR(nextFireTSR);
                __CFRunLoopTimerFireTSRUnlock();
            }
        }
    }
    else
    {
        // 解鎖 timer
        __CFRunLoopTimerUnlock(rlt);
    }
    // 釋放掉 timer
    CFRelease(rlt);
    return timerHandled;
}

其中真正執(zhí)行回調(diào)的是 CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION 函數(shù),其內(nèi)部實(shí)現(xiàn)如下:

static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__() __attribute__((noinline));
static void __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(CFRunLoopTimerCallBack func, CFRunLoopTimerRef timer, void *info)
{
    if (func)
    {
        func(timer, info);
    }
    asm __volatile__(""); // thwart tail-call optimization
}

CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE

static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__() __attribute__((noinline));
static void __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(void *msg)
{
    _dispatch_main_queue_callback_4CF(msg);
    asm __volatile__(""); // thwart tail-call optimization
}

總結(jié)

image.png

參考資料

深入理解 RunLoop - ibireme

解密 RunLoop - MrPeak

Core Foundation 詳解

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末杈帐,一起剝皮案震驚了整個(gè)濱河市体箕,隨后出現(xiàn)的幾起案子专钉,更是在濱河造成了極大的恐慌挑童,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跃须,死亡現(xiàn)場(chǎng)離奇詭異站叼,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)菇民,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)尽楔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人第练,你說(shuō)我怎么就攤上這事阔馋。” “怎么了娇掏?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵呕寝,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我婴梧,道長(zhǎng)下梢,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任塞蹭,我火速辦了婚禮孽江,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘番电。我一直安慰自己岗屏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布漱办。 她就那樣靜靜地躺著这刷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪洼冻。 梳的紋絲不亂的頭發(fā)上崭歧,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音撞牢,去河邊找鬼率碾。 笑死叔营,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的所宰。 我是一名探鬼主播绒尊,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼仔粥!你這毒婦竟也來(lái)了婴谱?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤躯泰,失蹤者是張志新(化名)和其女友劉穎谭羔,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體麦向,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘟裸,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了诵竭。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片话告。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卵慰,靈堂內(nèi)的尸體忽然破棺而出沙郭,到底是詐尸還是另有隱情,我是刑警寧澤裳朋,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布病线,位于F島的核電站,受9級(jí)特大地震影響再扭,放射性物質(zhì)發(fā)生泄漏氧苍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一泛范、第九天 我趴在偏房一處隱蔽的房頂上張望让虐。 院中可真熱鬧,春花似錦罢荡、人聲如沸赡突。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)惭缰。三九已至,卻和暖如春笼才,著一層夾襖步出監(jiān)牢的瞬間漱受,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工骡送, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昂羡,地道東北人絮记。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像虐先,于是被迫代替她去往敵國(guó)和親怨愤。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • 轉(zhuǎn)自bireme蛹批,原地址:https://blog.ibireme.com/2015/05/18/runloop/...
    乜_啊_閱讀 1,330評(píng)論 0 5
  • Runloop是iOS和OSX開(kāi)發(fā)中非匙矗基礎(chǔ)的一個(gè)概念,從概念開(kāi)始學(xué)習(xí)腐芍。 RunLoop的概念 -般說(shuō)差导,一個(gè)線程一...
    小貓仔閱讀 979評(píng)論 0 1
  • RunLoop 的概念 一般來(lái)講,一個(gè)線程一次只能執(zhí)行一個(gè)任務(wù)甸赃,執(zhí)行完成后線程就會(huì)退出柿汛。如果我們需要一個(gè)機(jī)制,讓線...
    Mirsiter_魏閱讀 614評(píng)論 0 2
  • 轉(zhuǎn)自http://blog.ibireme.com/2015/05/18/runloop 深入理解RunLoop ...
    飄金閱讀 975評(píng)論 0 4
  • 原文地址:http://blog.ibireme.com/2015/05/18/runloop/ RunLoop ...
    大餅炒雞蛋閱讀 1,148評(píng)論 0 6