這篇文章圍繞Core Foundation框架中關(guān)于run loop的源碼做一個(gè)深入理解赤惊。Core Fundation框架是開源的沿量,這里是它的源碼文件决侈。里面有關(guān)于run loop的CFRunLoop.h和CFRunLoop.m文件深员。這個(gè)框架是跨平臺的,可以看到很多適配windows和linux的宏定義宏浩。
Run Loop結(jié)構(gòu)
run loop在Core Foundation中是一個(gè)結(jié)構(gòu)體struct。它的定義如下:
struct __CFRunLoop {
CFRuntimeBase _base; //所有CF對象都包含的基礎(chǔ)結(jié)構(gòu)
pthread_mutex_t _lock; /* locked for accessing mode list 同步鎖*/
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp 內(nèi)核向該端口發(fā)送消息可以喚醒runloop
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread; //RunLoop對應(yīng)的線程
uint32_t _winthread; //作用同上 給windows平臺用
CFMutableSetRef _commonModes; //存儲的是字符串靠瞎,記錄所有標(biāo)記為common的mode
CFMutableSetRef _commonModeItems;//存儲所有commonMode的item(source比庄、timer、observer)
CFRunLoopModeRef _currentMode; //當(dāng)前運(yùn)行的mode
CFMutableSetRef _modes; //存儲的是CFRunLoopModeRef
struct _block_item *_blocks_head;//doblocks的時(shí)候用到
struct _block_item *_blocks_tail;
CFTypeRef _counterpart;
};
需要重點(diǎn)關(guān)注_commonModes乏盐、_commonModeItems佳窑、_currentMode、_modes父能;
可以看到他們都跟model有關(guān)神凑,model是run loop的模式,一個(gè)run loop可以含有多個(gè)model何吝。并且run loop默認(rèn)有一個(gè)commonModel 和一個(gè)currentModel溉委。 首先來看下model的定義
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name; //mode名稱
Boolean _stopped; //mode是否被終止
char _padding[3];
//幾種事件
CFMutableSetRef _sources0; //sources0
CFMutableSetRef _sources1; //sources1
CFMutableArrayRef _observers; //通知
CFMutableArrayRef _timers; //定時(shí)器
CFMutableDictionaryRef _portToV1SourceMap; //字典 key是mach_port_t,value是CFRunLoopSourceRef
__CFPortSet _portSet; //保存所有需要監(jiān)聽的port爱榕,比如_wakeUpPort瓣喊,_timerPort都保存在這個(gè)數(shù)組中
CFIndex _observerMask;
#if USE_DISPATCH_SOURCE_FOR_TIMERS
dispatch_source_t _timerSource;
dispatch_queue_t _queue;
Boolean _timerFired; // set to true by the source when a timer has fired
Boolean _dispatchTimerArmed;
#endif
#if USE_MK_TIMER_TOO
mach_port_t _timerPort;
Boolean _mkTimerArmed;
#endif
#if DEPLOYMENT_TARGET_WINDOWS
DWORD _msgQMask;
void (*_msgPump)(void);
#endif
uint64_t _timerSoftDeadline; /* TSR */
uint64_t _timerHardDeadline; /* TSR */
};
model中核心內(nèi)容有如下幾個(gè),source0,source1,observers,timers黔酥。source1表示基于Port端口事件藻三,包含了一個(gè) mach_port 和一個(gè)回調(diào)(函數(shù)指針)洪橘,當(dāng)線程處于休眠狀態(tài)時(shí), run loop會檢測這個(gè)端口棵帽,直到有消息進(jìn)入熄求,就會醒來去處理工作,sour0只包含了一個(gè)回調(diào)(函數(shù)指針)逗概,它并不能主動觸發(fā)事件弟晚。使用時(shí),需要先調(diào)用 CFRunLoopSourceSignal(source)逾苫,將這個(gè) Source 標(biāo)記為待處理指巡,然后手動調(diào)用 CFRunLoopWakeUp(runloop) 來喚醒 RunLoop來處理這個(gè)事件。timer是加入run loop的定時(shí)器隶垮,observer是run loop的狀態(tài)檢測者,他們可以通過檢測run loop的不同運(yùn)行狀態(tài)秘噪,觸發(fā)自己的回調(diào)狸吞。例如即將進(jìn)入run loop,即將進(jìn)入休眠,將要結(jié)束run loop等地方指煎,都可以進(jìn)行自定義回調(diào)蹋偏。這些source、timer和observer統(tǒng)稱為modelItem至壤。run loop中必須含有至少一個(gè)timer或source才能正常運(yùn)行威始,否則run loop會退出,后面的源碼將驗(yàn)證這一點(diǎn)像街。
它們的結(jié)構(gòu)如圖所示
run loop中包含數(shù)個(gè)model黎棠。 model中又包含數(shù)個(gè)source observer timer。run loop更是基于model運(yùn)行的镰绎,每次run loop運(yùn)行時(shí)候脓斩,有一個(gè)current model表明run loop的當(dāng)前運(yùn)行狀態(tài),只有在這個(gè)mdoel中的sourece畴栖、timer随静、observer才會被觸發(fā)。有個(gè)很典型的例子就是在tableview中的輪播圖定義器吗讶,將定時(shí)器加入run loop中燎猛,是加入到默認(rèn)的defaultmodel下的,所以正常情況下照皆,圖片可以輪播重绷,但是當(dāng)滑動tableview時(shí),系統(tǒng)會將run loop的運(yùn)行model切換成UITrackingModel,這時(shí)候膜毁,只有UITrackModel模式中的timer论寨、source星立、observer才會得到處理和回調(diào)。所以定時(shí)器會停止葬凳。如果想讓定時(shí)器繼續(xù)執(zhí)行绰垂,可以利用commonModels。
上面看到run loop中還有一個(gè)commonModels,它是一個(gè)比較特殊的結(jié)構(gòu)火焰,這個(gè)commonModels是一個(gè)set集合劲装,它存取的是字符串,字符串的名字是model的名字昌简,也就是run loop中其他model的名字占业。而commonModelItems里存的都是各個(gè)source、timer纯赎、observer谦疾。每當(dāng)將一個(gè)modelItem加入到commonModelItem時(shí)候,系統(tǒng)會自動將它同步添加到commonModels里存的名字所對應(yīng)的模型中犬金。因?yàn)橄到y(tǒng)已經(jīng)將defaultModel和UITrackingModel兩個(gè)模式的名字加入到了commonModels里念恍, 所以如果直接將timer加入到commonModels里,就等同于同時(shí)將tiemr加入同時(shí)加入到了兩個(gè)模式中晚顷,這樣無論是否滑動tableview峰伙,timer都會正常執(zhí)行。
看一下添加item的源碼,以添加定時(shí)器為例
//添加定時(shí)器
void CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef rlt, CFStringRef modeName) {
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return;
if (!__CFIsValid(rlt) || (NULL != rlt->_runLoop && rlt->_runLoop != rl)) return;
__CFRunLoopLock(rl);
//如果是往kCFRunLoopCommonModes中添加timer该默,則_commonModes中的所有mode都添加該timer
if (modeName == kCFRunLoopCommonModes) {
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
//獲取modeName對應(yīng)的mode瞳氓,如果mode不存在,則創(chuàng)建一個(gè)
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
//如果mode存在
if (NULL != rlm) {
//如果mode的_timers為空栓袖,則先初始化_timers
if (NULL == rlm->_timers) {
CFArrayCallBacks cb = kCFTypeArrayCallBacks;
cb.equal = NULL;
rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
}
}
//如果mode存在 && 該timer的_rlModes中不包含該_name匣摘,也就是說這個(gè)mode中沒有這個(gè)tiemr
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
__CFRunLoopTimerLock(rlt);
//如果timer的runloop為空
if (NULL == rlt->_runLoop) {
//則把傳入的runloop 賦值給timer對應(yīng)的runloop
rlt->_runLoop = rl;
}
//如果timer的runloop和傳入的runloop不一樣
else if (rl != rlt->_runLoop) {
//則什么都不處理,直接return
//也就是說一個(gè)timer只能被添加到一個(gè)runloop中裹刮,但是可以被添加到runloop的多個(gè)mode中
//PS:經(jīng)過測試驗(yàn)證,當(dāng)把一個(gè)timer添加到主線程的runloop之后恋沃,再往子線程的runloop添加該timer沒有效果,無法添加必指,timer一直運(yùn)行在主線程
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
return;
}
//把modeName記錄到timer的_rlModes中
CFSetAddValue(rlt->_rlModes, rlm->_name);
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
//注冊timer囊咏,向mode的timers中添加timer
__CFRepositionTimerInMode(rlm, rlt, false);
__CFRunLoopTimerFireTSRUnlock();
if (!_CFExecutableLinkedOnOrAfter(CFSystemVersionLion)) {
// Normally we don't do this on behalf of clients, but for
// backwards compatibility due to the change in timer handling...
if (rl != CFRunLoopGetCurrent()) CFRunLoopWakeUp(rl);
}
}
if (NULL != rlm) {
__CFRunLoopModeUnlock(rlm);
}
}
__CFRunLoopUnlock(rl);
}
其中CFSetApplyFunction函數(shù)會遍歷ComonModes集合,將timer添加到集合中的的每個(gè)Model。也就是加入到CommonModelItems中的每個(gè)item(包括timer source observer)都會被自動加入到所有Model中塔橡,包括DefaultModel和UITrackingModel梅割。
Run Loop獲取
可以看到Run Loop對象內(nèi)部是有很多結(jié)構(gòu)的,不同的結(jié)構(gòu)有不同作用葛家,首先關(guān)注下pthread_t _pthread户辞。這個(gè)是與run loop相關(guān)的線程。每一個(gè)run loop對象都有一個(gè)與之對象的線程癞谒,但是線程卻不一定有與之對應(yīng)的run loop,因?yàn)槟J(rèn)情況下底燎,新創(chuàng)建的線程是沒有run loop的刃榨,所以線程執(zhí)行完任務(wù)后,馬上會退出双仍,然后銷毀線程枢希。 線程可以創(chuàng)建并開啟run loop。并且只能創(chuàng)建一個(gè)run loop朱沃。 線程創(chuàng)建run loop的Api是CFRunLoopGetCurrent苞轿,它的內(nèi)部源碼如下:
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK (); //檢測進(jìn)程是否被fork
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop); //從線程上綁定的數(shù)據(jù)中 獲取run loop對象
if (rl) return rl; //run loop //如果線程上已經(jīng)綁定 直接返回該run loop對象
//傳入當(dāng)前線程
return _CFRunLoopGet0(pthread_self()); //創(chuàng)建
}
首先是檢查線程的私有數(shù)據(jù)池是否綁定了run loop,沒有綁定會調(diào)用run loop的創(chuàng)建函數(shù)_CFRunLoopGet0,并傳入當(dāng)前線程 pthread_self();
_CFRunLoopGet0的源碼如下:
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
//根據(jù)線程取RunLoop
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
//如果存儲runloop的字典不存在
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);
//創(chuàng)建一個(gè)臨時(shí)字典dict
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//創(chuàng)建主線程的runloop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//把主線程的runloop保存到dict中逗物,key是線程搬卒,value是runloop
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
//此處NULL和__CFRunLoops指針匹配,所以將dict寫到__CFRunLoops
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
//釋放dict
CFRelease(dict);
}
//釋放mainrunloop
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
//以上說明翎卓,第一次進(jìn)來的時(shí)候契邀,不管是getMainRunloop還是get子線程的runloop,主線程的runloop總是會被創(chuàng)建
//從字典__CFRunLoops中獲取傳入線程t的runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
//如果沒有獲取到
if (!loop) {
//根據(jù)線程t創(chuàng)建一個(gè)runloop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//把newLoop存入字典__CFRunLoops失暴,key是線程t
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFSpinUnlock(&loopsLock);
CFRelease(newLoop);
}
//如果傳入線程就是當(dāng)前線程
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
//注冊一個(gè)回調(diào)坯门,當(dāng)線程銷毀時(shí),銷毀對應(yīng)的runloop
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
這個(gè)方法首先檢測了傳入?yún)?shù)pthread_t t锐帜。如果t是空的,或者t等于0畜号,都會將t當(dāng)做主線程來處理缴阎。
KNilPthreadT類型是一個(gè)宏定義#define kNilPthreadT (pthread_t)0
接下來會檢測存儲run loop的字典__CFRunLoops是否存在,如果是第一次獲取run loop對象简软,那么這個(gè)字典肯定是不存在的蛮拔,就會調(diào)用創(chuàng)建字典的Api,并調(diào)用__CFRunLoopCreate函數(shù)創(chuàng)建主線程的run loop,并將主線程的run loop存入到字典中痹升。然后將字典使用原子操作賦值給__CFRunLoops建炫。
接下來是從字典中取run loop對象,如果是主線程疼蛾,那么run loop可以直接取到肛跌,如果是子線程,字典中現(xiàn)在還沒有該run loop對象察郁,所以會創(chuàng)建當(dāng)前線程的run loop對象衍慎。創(chuàng)建成功后,將run loop對象賦值給__CFRunLoops字典皮钠,其中key是線程的id稳捆。后面會調(diào)用線程api,將run loop綁定到線程上,同時(shí)注冊了一個(gè)回調(diào)麦轰,當(dāng)線程銷毀時(shí)候乔夯,會調(diào)用綁定到線程的回調(diào)函數(shù)砖织,在這個(gè)回調(diào)函數(shù)里,清除run loop對象以及其他對象末荐,也就是說run loop隨著線程的消亡而消亡侧纯。
線程和 RunLoop 之間是一一對應(yīng)的,其關(guān)系是保存在一個(gè)全局的 Dictionary 里鞠评。線程剛創(chuàng)建時(shí)并沒有 RunLoop茂蚓,如果不主動獲取,那它一直都不會有剃幌。RunLoop 的創(chuàng)建是發(fā)生在第一次獲取時(shí)聋涨,RunLoop 的銷毀是發(fā)生在線程結(jié)束時(shí)。只能在一個(gè)線程的內(nèi)部獲取其 RunLoop(主線程除外)负乡。
Run Loop創(chuàng)建
run loop的創(chuàng)建源碼如下:
//根據(jù)線程創(chuàng)建一個(gè)runloop
static CFRunLoopRef __CFRunLoopCreate(pthread_t t) {
CFRunLoopRef loop = NULL;
CFRunLoopModeRef rlm;
//計(jì)算一個(gè)內(nèi)存大小用于創(chuàng)建CFRunLoopRef對象
uint32_t size = sizeof(struct __CFRunLoop) - sizeof(CFRuntimeBase);
//用runtime創(chuàng)建一個(gè)CFRunLoopRef對象
loop = (CFRunLoopRef)_CFRuntimeCreateInstance(kCFAllocatorSystemDefault, __kCFRunLoopTypeID, size, NULL);
if (NULL == loop) {
return NULL;
}
//初始化runloop的各種標(biāo)記狀態(tài)
(void)__CFRunLoopPushPerRunData(loop);
__CFRunLoopLockInit(&loop->_lock);
//初始化一個(gè)喚醒端口牍白,當(dāng)需要喚醒runloop時(shí),可以通過內(nèi)核往該端口發(fā)送消息
loop->_wakeUpPort = __CFPortAllocate();
if (CFPORT_NULL == loop->_wakeUpPort) HALT;
//設(shè)置runloop為喚醒狀態(tài)
__CFRunLoopSetIgnoreWakeUps(loop);
//初始化_commonModes集合
loop->_commonModes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
//把kCFRunLoopDefaultMode加入_commonModes
CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode);
loop->_commonModeItems = NULL;
loop->_currentMode = NULL;
//初始化_modes集合
loop->_modes = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
loop->_blocks_head = NULL;
loop->_blocks_tail = NULL;
loop->_counterpart = NULL;
//線程賦值為傳入的線程
loop->_pthread = t;
#if DEPLOYMENT_TARGET_WINDOWS
loop->_winthread = GetCurrentThreadId();
#else
loop->_winthread = 0;
#endif
//查找kCFRunLoopDefaultMode抖棘,此處傳入true茂腥,所以沒找到會創(chuàng)建一個(gè)
rlm = __CFRunLoopFindMode(loop, kCFRunLoopDefaultMode, true);
if (NULL != rlm) __CFRunLoopModeUnlock(rlm);
return loop;
}
創(chuàng)建run loop的參數(shù)是線程pthread_t。首先調(diào)用Core Foundation創(chuàng)建對象的Api )_CFRuntimeCreateInstance創(chuàng)建run loop對象切省。然后按照run loop內(nèi)部的變量最岗,分別為它們初始化。
系統(tǒng)創(chuàng)建了一個(gè)commonModes集合用來存儲commonModels,但是這個(gè)集合只存儲名字朝捆。并不存儲實(shí)際的model對象般渡。 下一步調(diào)用CFSetAddValue(loop->_commonModes, kCFRunLoopDefaultMode); 將defaultmodel的名字添加進(jìn)入commonModels集合中,注意此時(shí) defaultmodel是空的芙盘。
下一步系統(tǒng)創(chuàng)建了models集合驯用,最后調(diào)用__CFRunLoopFindMode查找是否存在KCRunLoopDefaultMode,參數(shù)true表明,如果沒有找到model儒老,會根據(jù)名字創(chuàng)建一個(gè)蝴乔,所以此處會創(chuàng)建一個(gè)model。
這個(gè)方法創(chuàng)建并初始化了run loop對象驮樊。同時(shí)也為run loop內(nèi)部變量初始化薇正。
Run Loop運(yùn)行
在Core Foundation框架中關(guān)于Run loop運(yùn)行的Api有如下兩個(gè)個(gè):
CF_EXPORT void CFRunLoopRun(void);
CF_EXPORT CFRunLoopRunResult CFRunLoopRunInMode(CFRunLoopMode mode, CFTimeInterval seconds, Boolean returnAfterSourceHandled);
CFRunLoopRun是簡單的讓線程跑起來累榜,它內(nèi)部源碼:
//默認(rèn)運(yùn)行runloop的kCFRunLoopDefaultMode
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
//默認(rèn)在kCFRunLoopDefaultMode下運(yùn)行runloop
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
可以看到站刑,它定義了三個(gè)默認(rèn)參數(shù)運(yùn)行run loop掏膏。三個(gè)參數(shù)的含義分別是run loop對象, 運(yùn)行run loop的模式和超時(shí)時(shí)間嫩海,以及是否在處理完source后就返回胧谈,退出run loop鸿市。 這里是以默認(rèn)的defaultmodel,默認(rèn)超時(shí)時(shí)間1.e10,這個(gè)時(shí)間幾乎等于用不超時(shí)偿枕,然后開啟run loop, 并且因?yàn)榈谒膮?shù)設(shè)置為false,也就是run loop處理完source事件后苦囱,不會馬上返回鞠鲜,會繼續(xù)執(zhí)行宁脊。 while循環(huán)保證只要run loop不返回kCFRunLoopRunStopped和kCFRunLoopRunFinished断国,那么它始終不會退出。
CFRunLoopRunInMode是以指定參數(shù)運(yùn)行run loop,它的源碼:
//以指定模式運(yùn)行run loop
SInt32 CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
可以看到它內(nèi)部調(diào)用了CFRunLoopRunSpecific,將所有參數(shù)都傳給了這個(gè)Api榆苞。
這個(gè)CFRunLoopRunSpecific的源碼:
/*
* 指定mode運(yùn)行runloop
* @param rl 當(dāng)前運(yùn)行的runloop
* @param modeName 需要運(yùn)行的mode的name
* @param seconds runloop的超時(shí)時(shí)間
* @param returnAfterSourceHandled 是否處理完事件就返回
*/
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
//根據(jù)modeName找到本次運(yùn)行的mode
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
//如果沒找到 || mode中沒有注冊任何事件稳衬,則就此停止,不進(jìn)入循環(huán)
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
volatile _per_run_data *previousPerRun = __CFRunLoopPushPerRunData(rl);
//取_currentMode坐漏,保存為上一次運(yùn)行的mode
CFRunLoopModeRef previousMode = rl->_currentMode;
//把即將開始運(yùn)行的mode 賦值給_currentMode
rl->_currentMode = currentMode;
//初始化一個(gè)result為kCFRunLoopRunFinished
int32_t result = kCFRunLoopRunFinished;
// 1.通知observer即將進(jìn)入runloop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
//10.通知observer已退出runloop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
__CFRunLoopModeUnlock(currentMode);
__CFRunLoopPopPerRunData(rl, previousPerRun);
rl->_currentMode = previousMode;
__CFRunLoopUnlock(rl);
return result;
}
這部分源碼需要關(guān)注的重點(diǎn)有薄疚,1.如果model中沒有timer 和 source,run loop會直接退出赊琳,不會進(jìn)入循環(huán)街夭。2.run loop注冊的observer,會在這里回調(diào)即將進(jìn)入循環(huán) 和 已退出循環(huán)躏筏。 3.核心代碼是__CFRunLoopRun,并且會將上一次run loop的運(yùn)行模式傳入這個(gè)方法板丽。
接下來是run loop最核心的代碼 __CFRunLoopRun
因?yàn)檫@部分的源碼太長,而且涉及太多底層的東西趁尼,所以這里貼了兩份代碼埃碱,一份是網(wǎng)上大神博客中的縮減版的,一份是完整源碼酥泞,線上縮減版的:
{
/// 1. 通知Observers砚殿,即將進(jìn)入RunLoop
/// 此處有Observer會創(chuàng)建AutoreleasePool: _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopEntry);
do {
/// 2. 通知 Observers: 即將觸發(fā) Timer 回調(diào)。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeTimers);
/// 3. 通知 Observers: 即將觸發(fā) Source (非基于port的,Source0) 回調(diào)芝囤。
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeSources);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 4. 觸發(fā) Source0 (非基于port的) 回調(diào)似炎。
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__(source0);
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
/// 6. 通知Observers,即將進(jìn)入休眠
/// 此處有Observer釋放并新建AutoreleasePool: _objc_autoreleasePoolPop(); _objc_autoreleasePoolPush();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopBeforeWaiting);
/// 7. sleep to wait msg.
mach_msg() -> mach_msg_trap();
/// 8. 通知Observers凡人,線程被喚醒
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopAfterWaiting);
/// 9. 如果是被Timer喚醒的名党,回調(diào)Timer
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__(timer);
/// 9. 如果是被dispatch喚醒的叹阔,執(zhí)行所有調(diào)用 dispatch_async 等方法放入main queue 的 block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(dispatched_block);
/// 9. 如果如果Runloop是被 Source1 (基于port的) 的事件喚醒了挠轴,處理這個(gè)事件
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__(source1);
} while (...);
/// 10. 通知Observers,即將退出RunLoop
/// 此處有Observer釋放AutoreleasePool: _objc_autoreleasePoolPop();
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__(kCFRunLoopExit);
}
然后是完整版的
/* rl, rlm are locked on entrance and exit */
//運(yùn)行runloop
/**
* 運(yùn)行run loop
*
* @param rl 運(yùn)行的RunLoop對象
* @param rlm 運(yùn)行的mode
* @param seconds run loop超時(shí)時(shí)間
* @param stopAfterHandle true:run loop處理完事件就退出 false:一直運(yùn)行直到超時(shí)或者被手動終止
* @param previousMode 上一次運(yùn)行的mode
*
* @return 返回4種狀態(tài)
*/
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
//獲取系統(tǒng)啟動后的CPU運(yùn)行時(shí)間耳幢,用于控制超時(shí)時(shí)間
uint64_t startTSR = mach_absolute_time();
//如果RunLoop或者mode是stop狀態(tài)岸晦,則直接return,不進(jìn)入循環(huán)
if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
return kCFRunLoopRunStopped;
}
//mach端口睛藻,在內(nèi)核中启上,消息在端口之間傳遞。 初始為0
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)));
//如果在主線程 && runloop是主線程的runloop && 該mode是commonMode店印,則給mach端口賦值為主線程收發(fā)消息的端口
if (libdispatchQSafe && (CFRunLoopGetMain() == rl) && CFSetContainsValue(rl->_commonModes, rlm->_name)) dispatchPort = _dispatch_get_main_queue_port_4CF();
#if USE_DISPATCH_SOURCE_FOR_TIMERS
mach_port_name_t modeQueuePort = MACH_PORT_NULL;
if (rlm->_queue) {
modeQueuePort = _dispatch_runloop_root_queue_get_port_4CF(rlm->_queue);
if (!modeQueuePort) {
CRASH("Unable to get port for run loop mode queue (%d)", -1);
}
}
#endif
//GCD管理的定時(shí)器冈在,用于實(shí)現(xiàn)runloop超時(shí)機(jī)制
dispatch_source_t timeout_timer = NULL;
struct __timeout_context *timeout_context = (struct __timeout_context *)malloc(sizeof(*timeout_context));
//立即超時(shí)
if (seconds <= 0.0) { // instant timeout
seconds = 0.0;
timeout_context->termTSR = 0ULL;
}
//seconds為超時(shí)時(shí)間,超時(shí)時(shí)執(zhí)行__CFRunLoopTimeout函數(shù)
else if (seconds <= TIMER_INTERVAL_LIMIT) {
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, DISPATCH_QUEUE_OVERCOMMIT);
timeout_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_retain(timeout_timer);
timeout_context->ds = timeout_timer;
timeout_context->rl = (CFRunLoopRef)CFRetain(rl);
timeout_context->termTSR = startTSR + __CFTimeIntervalToTSR(seconds);
dispatch_set_context(timeout_timer, timeout_context); // source gets ownership of context
dispatch_source_set_event_handler_f(timeout_timer, __CFRunLoopTimeout); //dispatch_source_set_event_handler_f為函數(shù)形式調(diào)用按摘,dispatch_source_set_event_handle為block形式調(diào)用
dispatch_source_set_cancel_handler_f(timeout_timer, __CFRunLoopTimeoutCancel);
uint64_t ns_at = (uint64_t)((__CFTSRToTimeInterval(startTSR) + seconds) * 1000000000ULL);
dispatch_source_set_timer(timeout_timer, dispatch_time(1, ns_at), DISPATCH_TIME_FOREVER, 1000ULL);
dispatch_resume(timeout_timer);
}
//永不超時(shí)
else { // infinite timeout
seconds = 9999999999.0;
timeout_context->termTSR = UINT64_MAX;
}
//標(biāo)志位默認(rèn)為true
Boolean didDispatchPortLastTime = true;
//記錄最后runloop狀態(tài)包券,用于return
int32_t retVal = 0;
do {
//初始化一個(gè)存放內(nèi)核消息的緩沖池
uint8_t msg_buffer[3 * 1024];
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
mach_msg_header_t *msg = NULL;
mach_port_t livePort = MACH_PORT_NULL;
#elif DEPLOYMENT_TARGET_WINDOWS
HANDLE livePort = NULL;
Boolean windowsMessageReceived = false;
#endif
//取所有需要監(jiān)聽的port
__CFPortSet waitSet = rlm->_portSet;
//設(shè)置RunLoop為可以被喚醒狀態(tài)
__CFRunLoopUnsetIgnoreWakeUps(rl);
//2.通知observer纫谅,即將觸發(fā)timer回調(diào),處理timer事件
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
//3.通知observer溅固,即將觸發(fā)Source0回調(diào)
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
//執(zhí)行加入當(dāng)前runloop的block
__CFRunLoopDoBlocks(rl, rlm);
//4.處理source0事件
//有事件處理返回true付秕,沒有事件返回false
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
//執(zhí)行加入當(dāng)前runloop的block
__CFRunLoopDoBlocks(rl, rlm);
}
//如果沒有Sources0事件處理 并且 沒有超時(shí),poll為false
//如果有Sources0事件處理 或者 超時(shí)侍郭,poll都為true
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
//第一次do..whil循環(huán)不會走該分支询吴,因?yàn)閐idDispatchPortLastTime初始化是true
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
//從緩沖區(qū)讀取消息
msg = (mach_msg_header_t *)msg_buffer;
//接收dispatchPort端口的消息,(接收source1事件)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0)) {
//如果接收到了消息的話亮元,開始處理msg
goto handle_msg;
}
#elif DEPLOYMENT_TARGET_WINDOWS
if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
goto handle_msg;
}
#endif
}
didDispatchPortLastTime = false;
//6.通知觀察者RunLoop即將進(jìn)入休眠
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
//設(shè)置RunLoop為休眠狀態(tài)
__CFRunLoopSetSleeping(rl);
// do not do any user callouts after this point (after notifying of sleeping)
// Must push the local-to-this-activation ports in on every loop
// iteration, as this mode could be run re-entrantly and we don't
// want these ports to get serviced.
__CFPortSetInsert(dispatchPort, waitSet);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
#if USE_DISPATCH_SOURCE_FOR_TIMERS
do {
if (kCFUseCollectableAllocator) {
objc_clear_stack(0);
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
//接收waitSet端口的消息
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
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);
#else
if (kCFUseCollectableAllocator) {
objc_clear_stack(0);
memset(msg_buffer, 0, sizeof(msg_buffer));
}
msg = (mach_msg_header_t *)msg_buffer;
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY);
#endif
#elif DEPLOYMENT_TARGET_WINDOWS
// Here, use the app-supplied message queue mask. They will set this if they are interested in having this run loop receive windows messages.
__CFRunLoopWaitForMultipleObjects(waitSet, NULL, poll ? 0 : TIMEOUT_INFINITY, rlm->_msgQMask, &livePort, &windowsMessageReceived);
#endif
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
// 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.
__CFPortSetRemove(dispatchPort, waitSet);
__CFRunLoopSetIgnoreWakeUps(rl);
// user callouts now OK again
//取消runloop的休眠狀態(tài)
__CFRunLoopUnsetSleeping(rl);
//通知觀察者runloop被喚醒
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
//處理消息
handle_msg:;
__CFRunLoopSetIgnoreWakeUps(rl);
#if DEPLOYMENT_TARGET_WINDOWS
if (windowsMessageReceived) {
// These Win32 APIs cause a callout, so make sure we're unlocked first and relocked after
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
if (rlm->_msgPump) {
rlm->_msgPump();
} else {
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE | PM_NOYIELD)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
// To prevent starvation of sources other than the message queue, we check again to see if any other sources need to be serviced
// Use 0 for the mask so windows messages are ignored this time. Also use 0 for the timeout, because we're just checking to see if the things are signalled right now -- we will wait on them again later.
// NOTE: Ignore the dispatch source (it's not in the wait set anymore) and also don't run the observers here since we are polling.
__CFRunLoopSetSleeping(rl);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
__CFRunLoopWaitForMultipleObjects(waitSet, NULL, 0, 0, &livePort, NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
__CFRunLoopUnsetSleeping(rl);
// If we have a new live port then it will be handled below as normal
}
#endif
if (MACH_PORT_NULL == livePort) {
CFRUNLOOP_WAKEUP_FOR_NOTHING();
// handle nothing
} else if (livePort == rl->_wakeUpPort) {
CFRUNLOOP_WAKEUP_FOR_WAKEUP();
// do nothing on Mac OS
#if DEPLOYMENT_TARGET_WINDOWS
// Always reset the wake up port, or risk spinning forever
ResetEvent(rl->_wakeUpPort);
#endif
}
#if USE_DISPATCH_SOURCE_FOR_TIMERS
else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
#if USE_MK_TIMER_TOO
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
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
}
#endif
else if (livePort == dispatchPort) {
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
#if DEPLOYMENT_TARGET_WINDOWS
void *msg = 0;
#endif
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
didDispatchPortLastTime = true;
} else {
CFRUNLOOP_WAKEUP_FOR_SOURCE();
// Despite the name, this works for windows handles as well
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;
//處理source1事件
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);
}
#elif DEPLOYMENT_TARGET_WINDOWS
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
#endif
}
}
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
#endif
__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;
}
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
完整版的代碼很長猛计,涉及內(nèi)核交互的部分很多。但是核心就是調(diào)用mach_msg() -> mach_msg_trap() 使線程在沒有事情的時(shí)候苹粟,由用戶態(tài)切換到內(nèi)核態(tài)有滑,從而進(jìn)入休眠狀態(tài)。而有source時(shí)候嵌削,無論是基于mach端口的source1 還是 手動觸發(fā)的source0毛好,都會馬上喚醒run loop,讓run loop處理事件苛秕,run loop處理完事件后又會循環(huán).