一、RunLoop是什么
1. RunLoop概念
RunLoop是在程序运行中处理时间的循环, 每个线程都有一个Runloop与之对应, 当Runloop有事件需要处理时, 就会进行事件处理; 没有事件时, 线程就会进入休眠.
在iOS中, 有NSRunLoop和CFRunLoopRef两种, 而前者是后者的封装.
2. RunLoop的作用
RunLoop主要可以处理定时器, GCD, 事件响应, UI刷新, 网络请求以及AutoReleasePool等
3. RunLoop相关源码定义
Core Foundation中, RunLoop相关的有5个主要的类
- CFRunLoopRef
typedef struct __CFRunLoop * CFRunLoopRef;
struct __CFRunLoop {
pthread_mutex_t _lock; // 处理modes用到的锁
__CFPort _wakeUpPort; // 唤醒RunLoop的Port
pthread_t _pthread; // RunLoop对应的线程
CFMutableSetRef _commonModes; // commonModes模式下的modes集合
CFMutableSetRef _commonModeItems; // CommondModeItem 的集合
CFRunLoopModeRef _currentMode; // 当前运行的mode
CFMutableSetRef _modes; // 所有的modes
// ...
};
- CFRunLoopModeRef
- RunLoop在运行中只能运行在一个Mode中, 如果需要切换mode, 只能将当前mode停止, 然后用新mode重新启动;
- mode中存储的有当前的sources0, sources1, observer和timer等;
- 如果当前RunLoop中没有sources0等, 那么RunLoop就会立即退出
- Mode常见的有2中: kCFRunLoopDefaultMode(App的默认Mode, RunLoop通常会运行在这个Mode), UITrackingRunLoopMode(用于界面跟踪, 当ScrollView滑动时保证滑动不受其他Mode的影响)
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
pthread_mutex_t _lock; /* must have the run loop locked before locking this */
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _timers;
};
- CFRunLoopSourceRef
- Source0: 主要包含触摸事件, performSelector事件等
- Source1: 主要包含基于Port的线程通信, 系统事件捕捉
typedef struct __CFRunLoopSource * CFRunLoopSourceRef;
struct __CFRunLoopSource {
CFRuntimeBase _base;
uint32_t _bits;
pthread_mutex_t _lock;
CFIndex _order; /* immutable */
CFMutableBagRef _runLoops;
union {
CFRunLoopSourceContext version0; /* Source0 */
CFRunLoopSourceContext1 version1; /* Source1 */
} _context;
};
- CFRunLoopTimerRef
- NSTimer
- performSelector延迟执行事件
typedef struct __CFRunLoopTimer * CFRunLoopTimerRef;
struct __CFRunLoopTimer {
CFRuntimeBase _base;
uint16_t _bits;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFMutableSetRef _rlModes;
CFAbsoluteTime _nextFireDate; // 启动时间
CFTimeInterval _interval; // 时间间隔
CFTimeInterval _tolerance; /* mutable */
uint64_t _fireTSR; /* TSR units */
CFIndex _order; /* immutable */
CFRunLoopTimerCallBack _callout; /* immutable */
CFRunLoopTimerContext _context; /* immutable, except invalidation */
};
- CFRunLoopObserverRef
- 监听RunLoop状态的通知
- UI刷新的通知 (BeforeWaiting)
- Autorelease Pool 的通知 (BeforeWaiting)
typedef struct __CFRunLoopObserver * CFRunLoopObserverRef;
struct __CFRunLoopObserver {
CFRuntimeBase _base;
pthread_mutex_t _lock;
CFRunLoopRef _runLoop;
CFIndex _rlCount;
CFOptionFlags _activities; // Observer状态
CFIndex _order; /* immutable */
CFRunLoopObserverCallBack _callout; /* immutable */
CFRunLoopObserverContext _context; /* immutable, except invalidation */
};
/* Run Loop Observer Activities */
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0), // 进入
kCFRunLoopBeforeTimers = (1UL << 1), // 处理timer前
kCFRunLoopBeforeSources = (1UL << 2), // 处理source前
kCFRunLoopBeforeWaiting = (1UL << 5), // 休眠前
kCFRunLoopAfterWaiting = (1UL << 6), // 休眠后
kCFRunLoopExit = (1UL << 7), // 退出前
kCFRunLoopAllActivities = 0x0FFFFFFFU
};
4. 获取当前RunLoop
- CFRunLoopGetCurrent 通过CFRunLoopGetCurrent可以获取当前的RunLoop
CFRunLoopRef CFRunLoopGetCurrent(void) {
CHECK_FOR_FORK();
CFRunLoopRef rl = (CFRunLoopRef)_CFGetTSD(__CFTSDKeyRunLoop);
if (rl) return rl;
return _CFRunLoopGet0(pthread_self());
}
- _CFRunLoopGet0 如果直接找不到, 会调用这个方法查找, 并且参数是当前线程. 可以证明: RunLoop是与线程一一绑定的
static CFMutableDictionaryRef __CFRunLoops = NULL;
static CFLock_t loopsLock = CFLockInit;
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);
// 为主线程创建RunLoop
CFRunLoopRef mainLoop = __CFRunLoopCreate(pthread_main_thread_np());
// 将主线程与RunLoop加入字典中
CFDictionarySetValue(dict, pthreadPointer(pthread_main_thread_np()), mainLoop);
if (!OSAtomicCompareAndSwapPtrBarrier(NULL, dict, (void * volatile *)&__CFRunLoops)) {
CFRelease(dict);
}
CFRelease(mainLoop);
__CFLock(&loopsLock);
}
// 从全局字典中获取传入线程对应的RunLoop
CFRunLoopRef loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
__CFUnlock(&loopsLock);
if (!loop) {
// 如果全局字典中没有, 则传入当前线程t创建一个新的RunLoop
CFRunLoopRef newLoop = __CFRunLoopCreate(t);
__CFLock(&loopsLock);
loop = (CFRunLoopRef)CFDictionaryGetValue(__CFRunLoops, pthreadPointer(t));
if (!loop) {
// 将RunLoop与线程放入全局字典中
CFDictionarySetValue(__CFRunLoops, pthreadPointer(t), newLoop);
loop = newLoop;
}
__CFUnlock(&loopsLock);
// 将newLoop赋值给loop后释放
CFRelease(newLoop);
}
return loop;
}
二、RunLoop运行过程
文字描述版
- 通知Observer: 进入Loop
- 通知Observer: 即将处理Timer
- 通知Observer: 即将处理Source
- 处理Blocks
- 处理Source0, 可能再次处理Blocks
- 如果存在Source1, 就跳转到步骤8
- 通知Observer: 开始休眠, 等待被唤醒
- 通知Observer: 结束休眠, 处理响应
- 处理Timer
- 处理GCD消息
- 处理Source1
- 处理Blocks
- 根据前边的执行结果决定
- 回到步骤2继续Loop
- 退出Loop
- 通知Observer: 退出Loop
图片版本(图片来自网络)
源码验证
- CFRunLoopRun CFRunLoopRun中其实就是开启一个do-while循环, 如果result不是Stop或Finish, 就会一直循环下去
void CFRunLoopRun(void) { /* DOES CALLOUT */
int32_t result;
do {
result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
} while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}
- CFRunLoopRunSpecific
SInt32 CFRunLoopRunSpecific(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled) { /* DOES CALLOUT */
// 如果当前runloop被释放, 直接返回finish
if (__CFRunLoopIsDeallocating(rl)) return kCFRunLoopRunFinished;
__CFRunLoopLock(rl); // 加锁runloop
// 拿到runloop要进入的currentMode(已经加锁)
CFRunLoopModeRef currentMode = __CFRunLoopFindMode(rl, modeName, false);
// 当前没有mode, 或mode里边没有source/timer/observer等 就进行解锁并且返回
if (NULL == currentMode || __CFRunLoopModeIsEmpty(rl, currentMode, rl->_currentMode)) {
Boolean did = false;
if (currentMode) __CFRunLoopModeUnlock(currentMode);
__CFRunLoopUnlock(rl);
return did ? kCFRunLoopRunHandledSource : kCFRunLoopRunFinished;
}
// 将runloop的当前mode修改并记录
CFRunLoopModeRef previousMode = rl->_currentMode;
rl->_currentMode = currentMode;
// 初始化result
int32_t result = kCFRunLoopRunFinished;
// 如果当前当前mode是即将进入, 则通知 Observers :Entry (步骤1)
if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 进行runloop的内部函数
result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
// 如果当前当前mode是即将退出, 则通知 Observers :Exit (步骤11)
if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
// 解锁mode
__CFRunLoopModeUnlock(currentMode);
// 将之前的mode复原
rl->_currentMode = previousMode;
// 解锁runloop
__CFRunLoopUnlock(rl);
return result;
}
- __CFRunLoopRun内部函数
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
// 提前进行一些判断返回Stop
if (__CFRunLoopIsStopped(rl)) {
__CFRunLoopUnsetStopped(rl);
return kCFRunLoopRunStopped;
} else if (rlm->_stopped) {
rlm->_stopped = false;
return kCFRunLoopRunStopped;
}
// 初始化返回值
int32_t retVal = 0;
do {
// 设置runloop 取消 忽略唤醒 (可以接收消息)
__CFRunLoopUnsetIgnoreWakeUps(rl);
// 如果mode是Timer, 通知Observer: 即将处理Timer (步骤2)
if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
// 如果mode是Sources, 通知Observer: 即将处理Sources (步骤3)
if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// runloop处理Blocks (步骤4)
__CFRunLoopDoBlocks(rl, rlm);
// runloop处理Source0, 处理以后可能再次处理Blocks (步骤5)
Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
if (sourceHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
Boolean poll = sourceHandledThisLoop || (0ULL == timeout_context->termTSR);
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
msg = (mach_msg_header_t *)msg_buffer;
// 如果有基于MachPort的Source1, 则直接跳转到 handle_msg进行处理 (步骤6)
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
}
// 通知Observer: 即将开始休眠 (步骤7)
if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
__CFRunLoopSetSleeping(rl); // 进入休眠等待被唤醒
// 休眠时可以解锁runloop和mode
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
// 记录开始休眠的时间, 用于计算休眠了多久
CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
do {
msg = (mach_msg_header_t *)msg_buffer;
// 被MachPort唤醒
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
while (_dispatch_runloop_root_queue_perform_4CF(rlm->_queue));
if (rlm->_timerFired) {
// timer超时唤醒
rlm->_timerFired = false;
break;
} else {
if (msg && msg != (mach_msg_header_t *)msg_buffer) free(msg);
}
} else {
break;
}
} while (1);
// 被唤醒时要对runloop和mode重新加锁
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
// 计算runloop的休眠时间
rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));
// 设置runloop 忽略唤醒 (不在接收消息)
__CFRunLoopSetIgnoreWakeUps(rl);
// 通知Observer: runloop被唤醒 (步骤8)
__CFRunLoopUnsetSleeping(rl);
if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
// 处理被唤醒的消息
handle_msg:;
// 设置runloop 忽略唤醒 (不在接收消息)
__CFRunLoopSetIgnoreWakeUps(rl);
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
}
else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
CFRUNLOOP_WAKEUP_FOR_TIMER();
// 处理timer
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
// Re-arm the next timer
__CFArmNextTimerInMode(rlm, rl);
}
}
else if (livePort == dispatchPort) {
// 处理GCD
CFRUNLOOP_WAKEUP_FOR_DISPATCH();
__CFRunLoopModeUnlock(rlm);
__CFRunLoopUnlock(rl);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)6, NULL);
__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
_CFSetTSD(__CFTSDKeyIsInGCDMainQ, (void *)0, NULL);
__CFRunLoopLock(rl);
__CFRunLoopModeLock(rlm);
sourceHandledThisLoop = true;
didDispatchPortLastTime = true;
} else {
// 处理Source1
CFRUNLOOP_WAKEUP_FOR_SOURCE();
voucher_t previousVoucher = _CFSetTSD(__CFTSDKeyMachMessageHasVoucher, (void *)voucherCopy, os_release);
// Despite the name, this works for windows handles as well
CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);
if (rls) {
mach_msg_header_t *reply = NULL;
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);
}
}
}
// 处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
// 根据执行结果设置 retVal
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);
return retVal;
}