问题: 简述一下RunLoop 的相关内容.
回答目录:
- 基本作用.
- RunLoop与线程.
- RunLoop的底层结构.
- RunLoop的常用Mode.
- RunLoop的流程.
- RunLoop的主要应用.
本问题回答基于
CF-1153.18
版本.
RunLoop基本作用:
- 保证程序的持续运行
- 处理App中得各种事件(触摸事件、 定时器事件)
- 节省CPU资源,提高程序性能,用户态和内核态相互切换(该做事的时候做事,该休息的时候休息).
RunLoop与线程:
- 每一个线程有且都有一个与之对应的RunLoop对象.
- RunLoop保存在一个全局的NSDictionary, 线程作为key,RunLoop作为value.
- 线程刚创建的时候是没有RunLoop对象的,RunLoop会在第一次获取它时进行创建.
- RunLoop会在线程结束的时候自动销毁.
- 主线程的RunLoop默认开启(创建),子线程默认没有开启RunLoop.
RunLoop的数据结构:
一个 __CFRunLoop
结构体中里面含有多个 __CFRunLoopMode, 但是某一时刻有且只有一个mode才会生效.
struct __CFRunLoop {
CFMutableSetRef _commonModes;
CFMutableSetRef _commonModeItems;
CFRunLoopModeRef _currentMode;
CFMutableSetRef _modes;
}
CFRunLoopMode
的内部结构主要含有如下信息.
- mode的名称 _name;
- CFMutableSetRef类型的 _sources0;
- CFMutableSetRef类型的 _sources1;
- CFMutableArrayRef类型的 _observers;
- CFMutableArrayRef类型的 _times;
typedef struct __CFRunLoopMode *CFRunLoopModeRef;
struct __CFRunLoopMode {
CFStringRef _name;
CFMutableSetRef _sources0;
CFMutableSetRef _sources1;
CFMutableArrayRef _observers;
CFMutableArrayRef _times;
}
CFRunLoopModeRef
表示 RunLoop的运行模式.
-
一个RunLoop启动时,只能选择一个RunLoopMode作为
currentMode
. -
如果需要切换Mode,只能退出当前Loop,再重新选择一个Mode进入.
-
如果Mode中没有 Sources0 / Sources1 / Obersers / Timer, RunLoop会立马退出.
CFRunLoop的 常见Mode
- kCFRunLoopDefaultMode: App默认的Mode,通常主线程在这个Mode下运行.
- UITrackingRunLoopMode: 界面跟踪Mode,常用于 ScrollView 追踪触摸滑动.保证界面滑动是不受其他Mode影响.
- kCFRunLoopCommonMode: 包含 kCFRunLoopDefaultMode 和 UITrackingRunLoopMode 的Mode.
sources0
功能主要是:
- 触摸事件处理.
preformSelector: onThread:
sources1
功能主要是:
- 基于
Port
的线程之间通信. - 系统事件捕捉.
Timers
功能主要是:
- NSTimer
performSelect: withObject: afterDelay:
obervers
功能主要是:
- 用于监听RunLoop的状态
- UI刷新(BeforeWaiting)
RunLoop的主要流程如下所示.
RunLoop的执行流程主要是通过 CFRunLoopRunSpecific函数
进入.
SInt32 CFRunLoopRunSpecifc(CFRunLoopRef rl, CFStringRef modeName, CFTimeInterval seconds, Boolean returnAfterSourceHandled){
...
// 判断当前的观察者掩码模式,是Entry类型,则通知Observers,当前进入RunLoop.
if (currentMode->_observerMask & kCFRunLoopEntry) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
// 进入循环
__CFRunLoopRun(rl, currentModel, seconds, returnAfterSourceHandled, previousMode));
// 判断当前观察者掩码模式,是Exit类型,则通知Observers,当前退出RunLoop
if (currentMode->_oberverMask & kCFRunLoopExit)
__CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
...
}
在 __CFRunLoopRun
函数中,核心代码如下所示.
static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
...
int32_t retVal = 0;
do {
// 发送将要处理Times的通知
if (rlm->_observerMask & kCFRunLoopBeforeTimes) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimes);
// 发送将要处理Sources的通知
if (rlm->_oberverMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);
// 处理Blocks
__CFRunLoopBlocks(rl, rlm);
// 处理Sources0
Boolean sourcesHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
// 处理完Sources0,根据返回值确认是否再次处理Block
if (sourcesHandledThisLoop) {
__CFRunLoopDoBlocks(rl, rlm);
}
// 判断是否存在Source1, 如果存在则跳转到 handle_msg
if (MACH_PORT_NULL != dispatchPort && !didDispatchPortLastTime) {
#if DEPLOYMENT_TARGET_MACOSX || DEPLOYMENT_TARGET_EMBEDDED || DEPLOYMENT_TARGET_EMBEDDED_MINI
msg = (mach_msg_header_t *)msg_buffer;
if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
goto handle_msg;
}
#elif DEPLOYMENT_TARGET_WINDOWS
if (__CFRunLoopWaitForMultipleObjects(NULL, &dispatchPort, 0, 0, &livePort, NULL)) {
goto handle_msg;
}
#endif
}
// 发送将将要进入休眠的通知
if(!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting))
// 进入休眠
__CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);
// 发送将要结束休眠的通知
if(!poll && (rlm->_observerMask && kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
// 处理Sources1 相关事件
if (MACH_PORT_NULL == livePort) {
} else if (livePort == rl->_wakeUpPort) {
} else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
// 处理Timers事件
if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
__CFArmNextTimerInMode(rlm, rl);
}
} else if (livePort == dispatchPort) {
// 处理 GCD Async to Main Queue
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 {
...
// 处理 Sources0 相关事件
sourceHandledThisLoop = __CFRunLoopDoSource1(rl, rlm, rls) || sourceHandledThisLoop;
...
}
// 再次处理Blocks
__CFRunLoopDoBlocks(rl, rlm);
...
} while (0 == retVal);
...
}
Comments | 0 条评论