代碼筆記、知識擴展
iOS 開發(fā)中能遇到兩個線程對象: pthread_t 和 NSThread。過去蘋果有份文檔標明了 NSThread 只是 pthread_t 的封裝,但那份文檔已經(jīng)失效了,現(xiàn)在它們也有可能都是直接包裝自最底層的 mach thread。蘋果并沒有提供這兩個對象相互轉(zhuǎn)換的接口,但不管怎么樣,可以肯定的是 pthread_t 和 NSThread 是一一對應(yīng)的。比如,你可以通過 pthread_main_thread_np() 或 [NSThread mainThread] 來獲取主線程;也可以通過 pthread_self() 或 [NSThread currentThread] 來獲取當前線程。CFRunLoop 是基于 pthread 來管理的。
蘋果不允許直接創(chuàng)建 RunLoop,它只提供了兩個自動獲取的函數(shù):CFRunLoopGetMain() 和 CFRunLoopGetCurrent()。 這兩個函數(shù)內(nèi)部的邏輯大概是下面這樣:
/// 全局的Dictionary,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
/// 訪問 loopsDic 時的鎖
static CFSpinLock_t loopsLock;
/// 獲取一個 pthread 對應(yīng)的 RunLoop。
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次進入時,初始化全局Dic,并先為主線程創(chuàng)建一個 RunLoop。
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
/// 直接從 Dictionary 里獲取。
CFRunLoopRef loop = CFDictionaryGetValue(loopsDic, thread));
if (!loop) {
/// 取不到時,創(chuàng)建一個
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
/// 注冊一個回調(diào),當線程銷毀時,順便也銷毀其對應(yīng)的 RunLoop。
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
CFRunLoopRef CFRunLoopGetMain() {
return _CFRunLoopGet(pthread_main_thread_np());
}
CFRunLoopRef CFRunLoopGetCurrent() {
return _CFRunLoopGet(pthread_self());
}
綜上所述,線程和 RunLoop 之間是一一對應(yīng)的,其關(guān)系是保存在一個全局的 Dictionary 里,將當前線程作為鍵(key),RunLoop 作為值(value)。線程剛創(chuàng)建時并沒有 RunLoop,如果你不主動獲取,那它一直都不會有。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時,RunLoop 的銷毀是發(fā)生在線程結(jié)束時。你只能在一個線程的內(nèi)部獲取其 RunLoop(主線程除外)。