Skip to content

讲一下 Runloop 的内部实现逻辑? #106

@tbfungeek

Description

@tbfungeek

下面是 __CFRunLoopRun 的核心代码,这里为了理解方便已经简化了不必要的代码:

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) {
    //.....
    do {
        //......

        // 通知 observers: kCFRunLoopBeforeTimers, 即将处理 timers
        if (rlm->_observerMask & kCFRunLoopBeforeTimers) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
        // 通知 observers: kCFRunLoopBeforeSources, 即将处理 sources
        if (rlm->_observerMask & kCFRunLoopBeforeSources) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeSources);

        // 执行加入当前runloop的block
        __CFRunLoopDoBlocks(rl, rlm);
        // 处理 sources 0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
            // 如果实际处理了 sources 0,再一次处理blocks
            __CFRunLoopDoBlocks(rl, rlm);
        }
        //.....
        // 通知 observers: kCFRunLoopBeforeWaiting, 即将进入等待(睡眠)
        // 注意到如果实际处理了 source 0 或者超时了,不会进入睡眠,所以不会通知
        if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
        // 设置标志位,正在睡眠
        __CFRunLoopSetSleeping(rl);

        __CFRunLoopSetIgnoreWakeUps(rl);

        // user callouts now OK again
        __CFRunLoopUnsetSleeping(rl);

        // 通知 observers: kCFRunLoopAfterWaiting, 即停止等待(被唤醒)
        // 注意实际处理过 source 0 或者已经超时的话,不会通知(因为没有睡)
        if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
        // 被什么唤醒就处理什么:
            handle_msg:;
            __CFRunLoopSetIgnoreWakeUps(rl);

        //// 不知道哪个端口唤醒的(或者根本没睡),啥也不干
        if (MACH_PORT_NULL == livePort) {
            CFRUNLOOP_WAKEUP_FOR_NOTHING();
            // handle nothing
            //// 被 CFRunLoopWakeUp 函数弄醒的,啥也不干
        } else if (livePort == rl->_wakeUpPort) {
            CFRUNLOOP_WAKEUP_FOR_WAKEUP();
            // do nothing on Mac OS
        }
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer, because we apparently fired early
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
        // 被 GCD 唤醒处理 GCD
        else if (livePort == dispatchPort) {
            CFRUNLOOP_WAKEUP_FOR_DISPATCH();
            //.....
            __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            //....
        } else {
            /// 被 sources 1 唤醒,处理 sources 1
            CFRUNLOOP_WAKEUP_FOR_SOURCE();
            //.......
        }
        //....
    } while (0 == retVal);
    //....
    return retVal;
}
  1. 通知将要进入Runloop
  2. 通知将要处理Timmer事件
  3. 通知将要处理Source事件
  4. 处理Source0事件,在处理Source0事件之前都会调用****__CFRunLoopDoBlocks****处理下block。
  5. 通知将要进入睡眠状态
  6. Runloop进入睡眠状态
  7. 当有事件源给Runloop发送消息,表示有事件处理的时候,就会唤醒Runloop。
  8. 唤醒Runloop。
  9. 通知Runloop已经唤醒。
  10. 根据事件源来调用不同的方法来执行处理,这里的事件源可以是Timmer,Source1,GCD事件。
  11. 是否退出,Runloop ? 如果NO则,继续上面循环,如果YES则进入下一步
  12. 通知将要退出Runloop

大致如下图所示:

截屏2020-02-1414 46 15

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions