RunLoop概述
一般來講,一個線程一次只能執行一個任務,執行完成后線程就會退出。如果我們需要一個機制,讓線程能隨時處理事件但并不退出,通常的代碼邏輯是這樣的:
Event loop
RunLoop和Thread
-
RunLoop和線程的關系:
一個RunLoop對應著一條唯一的線程
-
RunLoop的創建:
MainThread上的RunLoop默認是創建好并啟動的,其它Thread中的RunLoop默認是沒有創建RunLoop
- RunLoop的生命周期:
在第一次獲取時創建,在線程結束時銷毀。除了MainThread外,其它Thread首次獲取該Thread的RunLoop時才被創建,代碼流程見下面
// 全局的 Dictionary ,key 是 pthread_t , value 是 CFRunLoopRef
static CFMutableDictionaryRef loopsDic;
// 訪問 loopsDic 時的線程鎖
static CFSpinLock_t loopsLock;
// 獲取一個 pthread 對應的 RunLoop
CFRunLoopRef _CFRunLoopGet(pthread_t thread) {
OSSpinLockLock(&loopsLock);
if (!loopsDic) {
// 第一次進入時,初始化全局 Dic ,并先為主線程創建一個RunLoop
loopsDic = CFDictionaryCreateMutable();
CFRunLoopRef mainLoop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, pthread_main_thread_np(), mainLoop);
}
// 直接從 Dictionary 里獲取 thread
CFRunLoopRef loop = CFDictionaryGetValue(loopsDis, thread);
if (!loop) {
// 如果取不到,創建一個
loop = _CFRunLoopCreate();
CFDictionarySetValue(loopsDic, thread, loop);
// 注冊一個回調,當線程銷毀時,順便也銷毀其對應的 RunLoop
_CFSetTSD(..., thread, loop, __CFFinalizeRunLoop);
}
OSSpinLockUnLock(&loopsLock);
return loop;
}
從上面的代碼可以看出,線程和RunLoop之間是一一對應的,其關系是保存在一個全局的Dictionary里。線程剛創建時并沒有RunLoop,如果你不主動獲取,那它一直都不會被創建。RunLoop的創建是發生在第一次獲取時,RunLoop的銷毀是發生在線程結束時。只能在一個線程的內部獲取其RunLoop(MainThread除外)。
注意:
- 開一個子線程創建RunLoop,不是通過alloc init方法創建,而是直接通過調用currentRunLoop方法來創建,它本身是一個懶加載的。
- RunLoop對象是利用字典來進行存儲的,而且key是對應的Thread,value為該Thread對應的RunLoop
- CFRunLoopRef源碼可以在CoreFoundation源碼中進行了解