RunLoop 是基础的一个概念,在 iOS 里它是由 CFRunLoop 实现。本章将从源码的方面梳理下RunLoop相关的概念、结构、原理。
浅谈RunLoop
RunLoop概念:
一般来讲,一个线程在执行任务完成后,就会结束。但是在我们的应用程序中不会直接结束,而是会在线程中构建一个消息循环机制。当有事件要去处理时保活线程,当没有事件要处理时让线程进入休眠,这个消息循环的机制就是RunLoop。
RunLoop和线程的关系:
- RunLoop是基于线程来管理的,所以是一一对应的关系
- 主线程中会自动获取Runloop,子线程默认不会获取Runloop
RunLoop创建:
苹果不允许直接创建 RunLoop,它只提供了两个自动获取的函数:CFRunLoopGetMain()
和 CFRunLoopGetCurrent()
,这两个函数最终会调用以下私有函数_CFRunLoopGet0()
。
相关源码如下:
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;
// should only be called by Foundation
// t==0 is a synonym for "main thread" that always works
CF_EXPORT CFRunLoopRef _CFRunLoopGet0(pthread_t t) {
if (pthread_equal(t, kNilPthreadT)) {
t = pthread_main_thread_np();
}
__CFLock(&loopsLock);
if (!__CFRunLoops) {
__CFUnlock(&loopsLock);
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorSystemDefault, 0, NULL, &kCFTypeDictionaryValueCallBacks);
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
// don't release run loops inside the loopsLock, because CFRunLoopDeallocate may end up taking it
__CFUnlock(&loopsLock);
CFRelease(newLoop);
}
if (pthread_equal(t, pthread_self())) {
_CFSetTSD(__CFTSDKeyRunLoop, (void *)loop, NULL);
if (0 == _CFGetTSD(__CFTSDKeyRunLoopCntr)) {
_CFSetTSD(__CFTSDKeyRunLoopCntr, (void *)(PTHREAD_DESTRUCTOR_ITERATIONS-1), (void (*)(void *))__CFFinalizeRunLoop);
}
}
return loop;
}
RunLoop结构
- CFRunLoopRef
- CFRunLoopModeRef
- CFRunLoopSourceRef
- CFRunLoopObserverRef
- CFRunLoopTimerRef
// CFRunLoopModeRef
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
// CFRunLoopRef
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoop * CFRunLoopRef;
// CFRunLoopSourceRef
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopSource * CFRunLoopSourceRef;
// CFRunLoopObserverRef
typedef struct CF_BRIDGED_MUTABLE_TYPE(id) __CFRunLoopObserver * CFRunLoopObserverRef;
// CFRunLoopTimerRef
typedef struct CF_BRIDGED_MUTABLE_TYPE(NSTimer) __CFRunLoopTimer * CFRunLoopTimerRef;
- CFRunLoopRef
CFRunLoopRef
包含了对应的线程,Mode集合,当前的Mode等等;
// 结构体定义
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
CFRunLoopModeRef
代表着RunLoop的运行模式,一个RunLoop中可以有多个mode,一个mode里面又可以有多个source、observer、timer等等。系统五个Mode:
- kCFRunLoopDefaultMode:App的默认Mode,通常主线程是在这个Mode下运行
- UITrackingRunLoopMode:界面跟踪 Mode,用于 ScrollView 追踪触摸滑动,保证界面滑动时不受其他 Mode 影响
- UIInitializationRunLoopMode: 在刚启动 App 时第进入的第一个 Mode,启动完成后就不再使用
- GSEventReceiveRunLoopMode: 接受系统事件的内部 Mode,通常用不到
- kCFRunLoopCommonModes: 这是一个占位用的Mode,不是一种真正的Mode
// 结构体定义
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 */
};
- CFRunLoopSourceRef
CFRunLoopSourceRef
是产生事件的地方。Source有两个版本:Source0 和 Source1。
// 结构体定义
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order; /* immutable */
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; /* immutable, except invalidation */
CFRunLoopSourceContext1 version1; /* immutable, except invalidation */
} _context;
};
Source0于Source1的区别如下:
- Source0 只包含了一个回调 ,它并不能主动触发事件
- Source1包含了一个 mach_port和一个回调 ,被用于通过内核和其他线程相互发送消息(系统消息,例如触摸手势,屏幕解锁,按键等等)这种Source 能主动唤醒RunLoop 的线程
// Source0
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
void (*schedule)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*cancel)(void *info, CFRunLoopRef rl, CFRunLoopMode mode);
void (*perform)(void *info);
} CFRunLoopSourceContext;
// Source1
typedef struct {
CFIndex version;
void * info;
const void *(*retain)(const void *info);
void (*release)(const void *info);
CFStringRef (*copyDescription)(const void *info);
Boolean (*equal)(const void *info1, const void *info2);
CFHashCode (*hash)(const void *info);
#if TARGET_OS_OSX || TARGET_OS_IPHONE
mach_port_t (*getPort)(void *info);
void * (*perform)(void *msg, CFIndex size, CFAllocatorRef allocator, void *info);
#else
void * (*getPort)(void *info);
void (*perform)(void *info);
#endif
} CFRunLoopSourceContext1;
- CFRunLoopObserverRef
CFRunLoopObserverRef
即是RunLoop的Observer,在它的结构体内部有一个_runLoop
成员和回调,每个Observer只能监听一个RunLoop,当 RunLoop 的状态发生变化时,Observer就会被触发回调。RunLoop的六个状态如下:
// 结构体定义
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities; /* immutable */
CFIndex _order; /* immutable */
CFRunLoopObserverCallBack _callout; /* immutable */
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 进入 loop
kCFRunLoopBeforeTimers = (1UL << 1), // 触发 Timer 回调
kCFRunLoopBeforeSources = (1UL << 2), // 触发 Source0 回调
kCFRunLoopBeforeWaiting = (1UL << 5), // 等待 mach_port 消息
kCFRunLoopAfterWaiting = (1UL << 6), // 接收 mach_port 消息
kCFRunLoopExit = (1UL << 7), // 退出 loop
kCFRunLoopAllActivities = 0x0FFFFFFFU // loop 所有状态改变
};
- CFRunLoopTimerRef
CFRunLoopTimerRef
是基于Timer的触发器,在它的结构体内部有一个时间间隔和回调,一个Timer仅能够添加到一个RunLoop中,当其加入到 RunLoop 时,RunLoop会注册对应的时间点,当时间间隔到达后,RunLoop
会被唤醒以执行该回调。
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFMutableSetRef _rlModes;
CFAbsoluteTime _nextFireDate;
CFTimeInterval _interval; /* immutable */
CFTimeInterval _tolerance; /* mutable */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable */
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
以上这些就是对RunLoop结构的介绍,下面我将结合CFRunLoop 的源码 来跟分析下 RunLoop 的原理吧。
RunLoop实现原理
首先,我将重要的函数调用顺序依次排开:
CFRunLoopRun()
->
CFRunLoopRunSpecific()
->
__CFRunLoopRun()
可以参考这个流程图
第一步:外层函数执行了do while,为线程后面loop做准备。 通知observers,RunLoop状态变化:kCFRunLoopEntry
,RunLoop 要开始进入循环了;
注意: 看这里的while条件
(kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result)
// CFRunLoopRun
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
CHECK_FOR_FORK();
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
// CFRunLoopRunSpecific
NSInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
CHECK_FOR_FORK();
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl);
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
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);
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
int32_t result = kCFRunLoopRunFinished;
// 通知 observers 状态发生变化,进入loop
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// RunLoop里面具体要做的事情, 主要是一个循环
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 通知 observers 状态发生变化,退出loop
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
__CFRunLoopModeUnlock(currentMode);
__CFRunLoopPopPerRunData(rl, previousPerRun);
rl->_currentMode = previousMode;
__CFRunLoopUnlock(rl);
return result;
}
第二步:开启一个 do while 来保活线程,通知 Observers,RunLoop状态变化: kCFRunLoopBeforeTimers
, 处理 Timer 回调
第三步:通知 Observers,RunLoop状态变化: kCFRunLoopBeforeSources
,处理 Source0 回调
第四步:然后执行加入RunLoop的 block回调,接着会触发 Source0 回调,接着可能会再次执行加入的 block回调,如果有 Source1 是 ready
状态的话,就会跳转到 handle_msg 去处理消息代码如下:
第五步:通知 Observers,RunLoop状态变化:kCFRunLoopBeforeWaiting
,线程将进入休眠(sleep)状态
第六步:进入休眠后,会等待 mach_port 的消息再次唤醒
第七步:通知 Observer,RunLoop状态变化:kCFRunLoopAfterWaiting
,接收到 mach_port 消息,RunLoop 的线程被唤醒了
第八步:RunLoop 被唤醒后开始处理消息了:
- 如果是 Timer 时间到了,触发 Timer 的回调
- 如果是 dispatch ,执行 block回调
- 如果是 source1 事件,处理这个事件回调
第九步:执行加入的 block回调
第十步:根据RunLoop的状态来判断是否需要走下一个 loop,结束循环 或者 回到外层循环进入第二步
// __CFRunLoopRun
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
uint64_t startTSR = mach_absolute_time();
int32_t retVal = 0;
do {
...
// 通知 Observers RunLoop 状态发生变化 触发 Timer 回调
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 通知 Observers RunLoop 状态发生变化 触发 Source0 回调
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 接着执行加入的 block回调
__CFRunLoopDoBlocks(rl, rlm);
// 接着触发 Source0 回调
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
// 可能会再次执行加入的 block回调
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
// 接着如果有 Source1 是 ready 状态的话,就会跳转到 handle_msg 去处理消息
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
}
...
//
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
...
// 等待唤醒
do {
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
// 基于port的source事件,调用者唤醒
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
// Timer 时间到、RunLoop 超时
if (rlm->_timerFired) {
rlm->_timerFired = false;
break;
} else {
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
break;
}
} while (1);
...
// 接收到 mach_port 消息, RunLoop 的线程被唤醒了
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
handle_msg:;
...
// 接着开始处理消息
// 如果 Timer 时间到,就触发 Timer 回调
if (msg-is-timer) {
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer, because we apparently fired early
__CFArmNextTimerInMode(rlm, rl);
}
}
// 如果 dispatch 就执行 block
else if (msg_is_dispatch) {
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
}
// Source1 事件的话,就处理这个事件
else {
CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
if (sourceHandledThisLoop) {
mach_msg(reply, MACH_SEND_MSG, reply);
}
}
...
// 执行加入的 block回调
__CFRunLoopDoBlocks(rl, rlm);
// loop 已经走完,这里要根据RunLoop 的状态来判断是否需要走下一个 loop,
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)) {
// mode 为空,RunLoop 结束
retVal = kCFRunLoopRunFinished;
}
} while (0 == retVal);
return retVal;
}