概念
RunLoop是事件接收和分發(fā)機制的一個實現(xiàn),是線程相關的基礎框架的一部分,一個RunLoop就是一個事件處理的循環(huán),用來不停的調(diào)度工作以及處理輸入事件。
RunLoop本質是一個do-while循環(huán),沒事做就休息,來活了就干活。與普通的while循環(huán)是有區(qū)別的,普通的while循環(huán)會導致CPU進入忙等待狀態(tài),即一直消耗cpu,而RunLoop則不會,RunLoop是一種閑等待,即RunLoop具備休眠功能。
RunLoop的作用
- 保持程序的持續(xù)運行
- 處理APP中的各種事件(觸摸、定時器、performSelector)
- 節(jié)省cpu資源、提供程序的性能:該做事就做事,該休息就休息
RunLoop源碼的下載地址
RunLoop和線程的關系
首先,iOS 開發(fā)中能遇到兩個線程對象:pthread_t和NSThread,兩者是一一對應的,可以通過pthread_main_thread_np()或 [NSThread mainThread]來獲取主線程;也可以通過 pthread_self()或[NSThread currentThread]來獲取當前線程。CFRunLoop是基于pthread來管理的。
蘋果不允許直接創(chuàng)建 RunLoop,它只提供了兩個自動獲取的函數(shù):CFRunLoopGetMain()和 CFRunLoopGetCurrent()。
// 主運行循環(huán)
CFRunLoopRef mainRunloop = CFRunLoopGetMain();
// 當前運行循環(huán)
CFRunLoopRef currentRunloop = CFRunLoopGetCurrent();
/********** CFRunLoopGetMain ********/
CFRunLoopRef CFRunLoopGetMain(void) {
CHECK_FOR_FORK();
static CFRunLoopRef __main = NULL; // no retain needed
//pthread_main_thread_np 主線程
if (!__main) __main = _CFRunLoopGet0(pthread_main_thread_np()); // no CAS needed
return __main;
}
// 保存RunLoop的全局字典,key 是 pthread_t, value 是 CFRunLoopRef
static CFMutableDictionaryRef __CFRunLoops = NULL;
/*********_CFRunLoopGet0 *******/
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
//如果t不存在,則標記為主線程(即默認情況,默認是主線程)
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFSpinLock(&loopsLock);
if (!__CFRunLoops) {
__CFSpinUnlock(&loopsLock);
// 第一次進入時,初始化全局Dic,并先為主線程創(chuàng)建一個 RunLoop。
//創(chuàng)建全局字典,標記為kCFAllocatorSystemDefault
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
//通過主線程 創(chuàng)建主運行循環(huán)
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
//利用dict,進行key-value綁定操作,即可以說明,線程和runloop是一一對應的
// dict : key value
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFSpinLock(&loopsLock);
}
//通過其他線程獲取runloop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFSpinUnlock(&loopsLock);
if (!loop) {
//如果沒有獲取到,則新建一個運行循環(huán)(所以子線程不獲取就不創(chuàng)建RunLoop)
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFSpinLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
//將新建的runloop 與 線程進行key-value綁定
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);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
// 注冊一個回調(diào),當線程銷毀時,順便也銷毀其對應的 RunLoop。
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
從上面的代碼可以看出
-
Runloop只有兩種,一種是主線程的, 一個是其他線程的 -
線程和RunLoop之間是一一對應的,其關系是保存在一個全局的 Dictionary里。 -
主線程的RunLoop已經(jīng)自動獲取(創(chuàng)建),子線程默認沒有開啟RunLoop
RunLoop的創(chuàng)建
根據(jù)CFRunLoopRef的定義得知RunLoop為一個對象,通過__CFRunLoop結構體進行創(chuàng)建
typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* locked for accessing mode list */
__CFPort _wakeUpPort; // used for CFRunLoopWakeUp
Boolean _unused;
volatile _per_run_data *_perRunData; // reset for runs of the run loop
pthread_t _pthread; // runloop和線程是一一對應關系,每個runloop內(nèi)部會保留一個對應的線程
uint32_t _winthread;
CFMutableSetRef _commonModes;//標記為common的mode的集合
CFMutableSetRef _commonModeItems;//commonMode的item集合
CFRunLoopModeRef _currentMode;// 當前的模式
CFMutableSetRef _modes;// CFRunLoopModeRef類型的集合,相對NSArray有序,Set為無序集合
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFTypeRef _counterpart;
};
/********** __CFRunLoopMode *************/
struct __CFRunLoopMode {
CFRuntimeBase _base;
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
Boolean _stopped;
char _padding[3];
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
CFMutableDictionaryRef _portToV1SourceMap;
__CFPortSet _portSet;
CFIndex _observerMask;
};

根據(jù)
__CFRunLoop與__CFRunLoopMode結構體屬性可以看出,每個RunLoop中包含了多個Mode,Mode里面又包含了多個Source/Observer/Timer,其中source分為source0和source1, 多個mode里,有且僅有一個為currentMode。如果要切換Mode,只能退出當前 Loop,再重新指定一個Mode進入。這樣做主要是為了分隔開不同組的 Source/Timer/Observer,讓其互不影響。
ModeItem
Source & Timer & Observer統(tǒng)稱為item,一個item可以被同時加入多個mode。但一個 item被重復加入同一個 mode時是不會有效果的。如果一個mode中一個item 都沒有,則RunLoop會直接退出,不進入循環(huán)
-
Source表示可以喚醒RunLoop的一些事件,基于CFRunLoopSourceRef,是事件產(chǎn)生的地方,分為source0與source1-
source0:表示非系統(tǒng)事件,即用戶自定義的事件,只包含了一個回調(diào),它并不能主動觸發(fā)事件。使用時,你需要先調(diào)用CFRunLoopSourceSignal(source),將這個Source標記為待處理,然后手動調(diào)用CFRunLoopWakeUp(runloop)來喚醒RunLoop,讓其處理這個事件。 -
source1:表示系統(tǒng)事件,主要負責底層的通訊,具備喚醒能力,包含了一個 mach_port和一個回調(diào),被用于通過內(nèi)核和其他線程相互發(fā)送消息。這種Source能主動喚醒RunLoop的線程。
-
Timer:就是常用NSTimer定時器這一類,基于CFRunLoopTimerRef,是基于時間的觸發(fā)器,它和NSTimer是toll-free bridged的,可以混用。其包含一個時間長度和一個回調(diào)(函數(shù)指針)。當其加入到RunLoop時,RunLoop會注冊對應的時間點,當時間點到時,RunLoop會被喚醒以執(zhí)行那個回調(diào)。
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits; //標記fire狀態(tài)
pthread_mutex_t _lock;
CFRunLoopRef _runLoop; //添加該timer的runloop
CFMutableSetRef _rlModes; //存放所有 包含該timer的 mode的 modeName,意味著一個timer可能會在多個mode中存在
CFAbsoluteTime _nextFireDate; // 下一次觸發(fā)時間
CFTimeInterval _interval; //執(zhí)行的時間間隔
CFTimeInterval _tolerance; //時間偏差
uint64_t _fireTSR; // 觸發(fā)時間
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; // 回調(diào)
CFRunLoopTimerContext _context; // 回調(diào)內(nèi)容
};
-
Observer: 主要用于監(jiān)聽RunLoop的狀態(tài)變化,并作出一定響應,基于CFRunLoopObserverRef,是觀察者,結構體內(nèi)部包含著一個_runloop成員, 表明每個Observer同時只能監(jiān)聽一個RunLoop,每個 Observer都包含了一個回調(diào),當RunLoop的狀態(tài)發(fā)生變化時,觀察者就能通過回調(diào)接受到這個變化,主要有這幾個狀態(tài):
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
//進入RunLoop
kCFRunLoopEntry = (1UL << 0),
//即將處理Timers
kCFRunLoopBeforeTimers = (1UL << 1),
//即將處理Source
kCFRunLoopBeforeSources = (1UL << 2),
//即將進入休眠
kCFRunLoopBeforeWaiting = (1UL << 5),
//被喚醒
kCFRunLoopAfterWaiting = (1UL << 6),
//退出RunLoop
kCFRunLoopExit = (1UL << 7),
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
- Item類型
block應用:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__調(diào)用timer:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__響應source0:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__響應source1:
__CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION__GCD主隊列:
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__observer源:
__CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__
可以根據(jù)蘋果官方文檔針對RunLoop處理不同源的圖示

- 管理item接口
CFRunLoopAddSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopAddObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopAddTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
CFRunLoopRemoveSource(CFRunLoopRef rl, CFRunLoopSourceRef source, CFStringRef modeName);
CFRunLoopRemoveObserver(CFRunLoopRef rl, CFRunLoopObserverRef observer, CFStringRef modeName);
CFRunLoopRemoveTimer(CFRunLoopRef rl, CFRunLoopTimerRef timer, CFStringRef mode);
以Timer為例
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
if (modeName == kCFRunLoopCommonModes) {
//如果是kCFRunLoopCommonModes 類型
CFSetRef set = rl->_commonModes ? CFSetCreateCopy(kCFAllocatorSystemDefault, rl->_commonModes) : NULL;
if (NULL == rl->_commonModeItems) {
rl->_commonModeItems = CFSetCreateMutable(kCFAllocatorSystemDefault, 0, &kCFTypeSetCallBacks);
}
//runloop與mode 是一對多的, mode與item也是一對多的
CFSetAddValue(rl->_commonModeItems, rlt);
if (NULL != set) {
CFTypeRef context[2] = {rl, rlt};
/* add new item to all common-modes */
//執(zhí)行
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
//如果是非commonMode類型
//查找runloop的模型
CFRunLoopModeRef rlm = __CFRunLoopFindMode(rl, modeName, true);
if (NULL != rlm) {
if (NULL == rlm->_timers) {
CFArrayCallBacks cb = kCFTypeArrayCallBacks;
cb.equal = NULL;
rlm->_timers = CFArrayCreateMutable(kCFAllocatorSystemDefault, 0, &cb);
}
}
//判斷mode是否匹配
if (NULL != rlm && !CFSetContainsValue(rlt->_rlModes, rlm->_name)) {
__CFRunLoopTimerLock(rlt);
if (NULL == rlt->_runLoop) {
rlt->_runLoop = rl;
} else if (rl != rlt->_runLoop) {
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
return;
}
// 如果匹配,則將runloop加進去,而runloop的執(zhí)行依賴于 [runloop run]
CFSetAddValue(rlt->_rlModes, rlm->_name);
__CFRunLoopTimerUnlock(rlt);
__CFRunLoopTimerFireTSRLock();
__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);
}
RunLoop 執(zhí)行流程
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 通知Observers, 進入RunLoop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// RunLoop里面具體要做的事情, 主要是一個循環(huán)
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知Observers, 退出 RunLoop
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
return result;
}
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
mach_port_name_t dispatchPort = MACH_PORT_NULL;
// 當前是否為主線程
bool cond1 = pthread_main_np();
// _CFGetTSD(__CFTSDKeyIsInGCDMainQ) 從預先分配的插槽中獲取主線程的一些特定數(shù)據(jù)
// CF源碼中沒有搜索到_CFSetTSD(__CFTSDKeyIsInGCDMainQ)的調(diào)用,推測但不完全確定_CFGetTSD(__CFTSDKeyIsInGCDMainQ) 為空
bool cond2 = 0 == _CFGetTSD(__CFTSDKeyIsInGCDMainQ);
// 當前是否為主線程
bool cond3 = CFRunLoopGetMain() == rl;
// 當前的mode包含在commonModes里面
bool cond4 = CFSetContainsValue(rl->_commonModes, rlm->_name);
// 當前為主線程且mode被標記為common,dispatchPort才會被賦值
if (cond1 && cond2 && cond3 && cond4) {
dispatchPort = _dispatch_get_main_queue_port_4CF();
}
Boolean didDispatchPortLastTime = true;
int32_t retVal = 0;
do {
__CFRunLoopUnsetIgnoreWakeUps(rl);
// 通知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);
}
// timeout 傳入0 表示立即返回 傳入TIMEOUT_INFINITY 表示等待到消息再返回
// 當前為主線程且mode被標記為common,且非第一次循環(huán)
if (dispatchPort && !didDispatchPortLastTime) {
// 主線程里有消息處理,直接跳轉到handle_msg
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
}
didDispatchPortLastTime = false;
// 通知observers, 即將進入休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl);
// 線程進入休眠,等待喚醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
__CFRunLoopUnsetSleeping(rl);
// 通知observers, 已經(jīng)結束休眠
__CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
// 處理消息
handle_msg:;
if (msg_is_timer) {
// 處理timer
__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())
} else if (msg_is_dispatch) {
// 處理GCD 如果有dispatch到main_queue的block,執(zhí)行block
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
} else {
// 處理Source1
__CFRunLoopDoSource1(rl, rlm, rls, msg, msg->msgh_size, &reply)
}
// 處理Blocks
__CFRunLoopDoBlocks(rl, rlm);
// 設置返回值
if (sourceHandledThisLoop && stopAfterHandle) {
//stopAfterHandle 是傳入的標記,如果處理完事件就返回
retVal = kCFRunLoopRunHandledSource;
} else if (timeout_context->termTSR < mach_absolute_time()) {
//設置了超時時間,超時返回
retVal = kCFRunLoopRunTimedOut;
} else if (__CFRunLoopIsStopped(rl)) {
// 被外部調(diào)用者強制停止了
__CFRunLoopUnsetStopped(rl);
retVal = kCFRunLoopRunStopped;
} else if (__CFRunLoopModeIsEmpty(rl, rlm, previousMode)) {
// 內(nèi)部source,observers,timers 都空了,返回
retVal = kCFRunLoopRunFinished;
}
// 如果不進入上述的條件,繼續(xù)循環(huán)
} while (0 == retVal);
if (timeout_timer) {
dispatch_source_cancel(timeout_timer);
dispatch_release(timeout_timer);
} else {
free(timeout_context);
}
return retVal;
}
