问题: 简述一下RunLoop 的相关内容.


回答目录:

  • 基本作用.
  • RunLoop与线程.
  • RunLoop的底层结构.
  • RunLoop的常用Mode.
  • RunLoop的流程.
  • RunLoop的主要应用.

本问题回答基于 CF-1153.18 版本.

RunLoop基本作用:

  1. 保证程序的持续运行
  2. 处理App中得各种事件(触摸事件、 定时器事件)
  3. 节省CPU资源,提高程序性能,用户态和内核态相互切换(该做事的时候做事,该休息的时候休息).

RunLoop与线程:

  1. 每一个线程有且都有一个与之对应的RunLoop对象.
  2. RunLoop保存在一个全局的NSDictionary, 线程作为key,RunLoop作为value.
  3. 线程刚创建的时候是没有RunLoop对象的,RunLoop会在第一次获取它时进行创建.
  4. RunLoop会在线程结束的时候自动销毁.
  5. 主线程的RunLoop默认开启(创建),子线程默认没有开启RunLoop.

RunLoop的数据结构:

一个 __CFRunLoop 结构体中里面含有多个 __CFRunLoopMode, 但是某一时刻有且只有一个mode才会生效.

struct __CFRunLoop {
    CFMutableSetRef _commonModes;
    CFMutableSetRef _commonModeItems;
    CFRunLoopModeRef _currentMode;
    CFMutableSetRef _modes;
}

CFRunLoopMode 的内部结构主要含有如下信息.

  1. mode的名称 _name;
  2. CFMutableSetRef类型的 _sources0;
  3. CFMutableSetRef类型的 _sources1;
  4. CFMutableArrayRef类型的 _observers;
  5. 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

  1. kCFRunLoopDefaultMode: App默认的Mode,通常主线程在这个Mode下运行.
  2. UITrackingRunLoopMode: 界面跟踪Mode,常用于 ScrollView 追踪触摸滑动.保证界面滑动是不受其他Mode影响.
  3. 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);

...

}

IT界无底坑洞栋主 欢迎加Q骚扰:676758285