Runloop 介绍
从字面的意思来看Runloop是运行循环,而它也是一个对象,可以提供一个入口函数,程序就会进入一个do...while循环,保证我们当前应用不被退出。
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
Runloop其实就是一种消息机制处理模式,比如,我点击一下屏幕、调用一个Timer、或执行performSeletor一个方法,这些都属于消息机制,但它都会交给Runloop进行处理。
Runloop 的作用
- 保持程序的持续运行
- 处理APP中的各种事件(触摸、定时器、performSeletor)
- 节省CPU资源、提高程序的性能:有活就干,没活就休息

Runloop 的item:
-
block应用:CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK
-
调用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
以上这些源都会调用我们Runloop的底层。
Runloop和线程的关系
线程和Runloop通过一个可变的全局CFMutableDictionaryRef进行Key-Value的存储,所以他们之间的关系是一一对应。
Runloop的创建
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
Runloop的存储
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
Runloop的获取
CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
注意:
- 子线程的Runloop默认不开启
- 事件源依赖于Runloop
Runloop 源码分析
Runloop是一个对象结构体,是利用线程创建的,默认的mode是kCFRunLoopDefaultMode。
- CFRunLoopRef
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;
uint32_t _winthread;
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
struct _block_item *_blocks_head;
struct _block_item *_blocks_tail;
CFAbsoluteTime _runTime;
CFAbsoluteTime _sleepTime;
CFTypeRef _counterpart;
};
- CFRunLoopModeRef
mode的类型:
- kCFRunLoopDefaultMode
- UITrackingRunLoopMode
- UIInitializationRunLoopMode
- GSEventReceiveRunLoopMode
- kCFRunLoopCommonModes
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;
#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 */
};
那我们总结一下Runloop的结构,如下图:

首先,Runloop对象与线程是一一对应关系,并且Runloop是依赖于线程,所以操作线程就可以控制Runloop。 其次,RunLoop定义的结构体中包括一个CFRunloopMode的人集合,所以,他们之间是一对的关系。 最后,CFRunLoopMode定义的结构体中包括多个源的集合,所以,mode与各种源也是一对多的关系。
注意:程序中一次运行只能依赖一个mode
一个Mode包含的items:
- CFRunLoopAddTimer
- CFRunLoopAddObserver
- CFRunLoopAddSource
- CFSetAddValue(rl->_commonModeItems, rlt) 储存Item到对应的mode里面
- __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) 调用我们之前存储的Item
- doIt = CFEqual
- doIt = CFSetContainsValue
- CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK(block)
- block()
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
if (!rl->_blocks_head) return false;
if (!rlm || !rlm->_name) return false;
Boolean did = false;
struct _block_item *head = rl->_blocks_head;
struct _block_item *tail = rl->_blocks_tail;
rl->_blocks_head = NULL;
rl->_blocks_tail = NULL;
CFSetRef commonModes = rl->_commonModes;
CFStringRef curMode = rlm->_name;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
struct _block_item *prev = NULL;
struct _block_item *item = head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
// timer 加入的mode 和 我们现在runloop的mode 相等
// curr->_mode = kCFRunLoopCommonModes 相等
// 事务就能执行
doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
}
if (!doit) prev = curr;
if (doit) {
if (prev) prev->_next = item;
if (curr == head) head = item;
if (curr == tail) tail = prev;
void (^block)(void) = curr->_block;
CFRelease(curr->_mode);
free(curr);
if (doit) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
did = true;
}
Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
}
}
- CFRunloopTimer
timer 的底层就是一个CFRunLoopTimerRef,这个timer是受我们的Runloop模式切换的影响
- timer 一定要加入相应的Runloop 的mode中,底层就是将timer加到items
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);
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 */
// timer -- items()
CFSetApplyFunction(set, (__CFRunLoopAddItemToCommonModes), (void *)context);
CFRelease(set);
}
} else {
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);
}
}
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;
}
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 run的时候,就会执行doblock,进入while循环,遍历item->next,判断doit成立后,对我们当前的item进行一个block调用。
static Boolean __CFRunLoopDoBlocks(CFRunLoopRef rl, CFRunLoopModeRef rlm) { // Call with rl and rlm locked
if (!rl->_blocks_head) return false;
if (!rlm || !rlm->_name) return false;
Boolean did = false;
struct _block_item *head = rl->_blocks_head;
struct _block_item *tail = rl->_blocks_tail;
rl->_blocks_head = NULL;
rl->_blocks_tail = NULL;
CFSetRef commonModes = rl->_commonModes;
CFStringRef curMode = rlm->_name;
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
struct _block_item *prev = NULL;
struct _block_item *item = head;
while (item) {
struct _block_item *curr = item;
item = item->_next;
Boolean doit = false;
if (CFStringGetTypeID() == CFGetTypeID(curr->_mode)) {
// timer 加入的mode 和 我们现在runloop的mode 相等
// curr->_mode = kCFRunLoopCommonModes 相等
// 事务就能执行
doit = CFEqual(curr->_mode, curMode) || (CFEqual(curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
} else {
doit = CFSetContainsValue((CFSetRef)curr->_mode, curMode) || (CFSetContainsValue((CFSetRef)curr->_mode, kCFRunLoopCommonModes) && CFSetContainsValue(commonModes, curMode));
}
if (!doit) prev = curr;
if (doit) {
if (prev) prev->_next = item;
if (curr == head) head = item;
if (curr == tail) tail = prev;
void (^block)(void) = curr->_block;
CFRelease(curr->_mode);
free(curr);
if (doit) {
__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__(block);
did = true;
}
Block_release(block); // do this before relocking to prevent deadlocks where some yahoo wants to run the run loop reentrantly from their dealloc
}
}
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
if (head) {
tail->_next = rl->_blocks_head;
rl->_blocks_head = head;
if (!rl->_blocks_tail) rl->_blocks_tail = tail;
}
return did;
}
- CFRunloopObserver
监听当前Runloop的状态,只要发生变化,就会触发observer的回调,如果没有任务执行,会进入休眠状态
- source0
包含回调函数指针
- signal 待处理
- wakeup 唤醒Runloop 处理事件
主要用于app内部事件,自己负责管理 的事务(UIEvent、CFSocket)
调用底层:
- 因为source0只包含函数指针,它并不能主动触发事件
- CFRunLoopSourceSignal(source)将这个事件标记为待处理
- CFRunLoopWakeUp来唤醒Runloop,让他处理事件
CFRunLoopSOurceContext
创建Source 0 :
CFRunLoopSourceRef source0 = CFRunLoopSourceCreate(CFAllocatorGetDefault(), 0, &context);
添加到Runloop:
CFRunLoopRef rlp = CFRunLoopGetCurrent();
// source --> runloop 指定了mode 那么此时我们source就进入待绪状态
CFRunLoopAddSource(rlp, source0, kCFRunLoopDefaultMode);
执行信号:
// 一个执行信号
CFRunLoopSourceSignal(source0);
唤醒Runloop去处理:
// 唤醒 run loop 防止沉睡状态
CFRunLoopWakeUp(rlp);
取消移除源:
// 取消 移除
CFRunLoopRemoveSource(rlp, source0, kCFRunLoopDefaultMode);
CFRlease(rlp)
- source1
-
包含一个mach_port & 回调函数指针
-
被用于通过内核和其他线程相互发送消息
Runloop 原理
任何的事件源处理都是通过Observers 监听通知Runloop运行的。
如下图:
