RunLoop系列面试题- 3.RunLoop的具体流程

RunLoop的具体流程或者说_source0,_source1,_observer,_timer都干了些什么

source0: 主要负责触摸事件,performSelector等等

source1: 线程间通讯,系统事件捕获(比如屏幕点击等等)

observer: RunLoop的状态变化,和UI刷新(休眠之前),autorelease(休眠之前)

timer: NSTimer里面的操作,延时函数

要想知道流程,首先就得找到源码的入口

import UIKit

class ViewController: UIViewController {

    @objc func test()
    {
        print("1")
    }
    override func viewDidLoad() {
        super.viewDidLoad()
        self.perform(Selector("test"), with: nil, afterDelay: 1.0)
        // Do any additional setup after loading the view.
    }
}

比如新建个项目写个延时函数,然后打断点到test里面,用指令bt输出函数调用栈

frame #4: 0x000000010a55e1c2 CoreFoundation`__CFRunLoopDoTimer + 923
frame #5: 0x000000010a55d781 CoreFoundation`__CFRunLoopDoTimers + 265
frame #6: 0x000000010a557dc0 CoreFoundation`__CFRunLoopRun + 2010
frame #7: 0x000000010a557103 CoreFoundation`CFRunLoopRunSpecific + 567
frame #8: 0x0000000109926cd3 GraphicsServices`GSEventRunModal + 139
frame #9: 0x00000001123f7e63 UIKitCore`-[UIApplication _run] + 928
frame #10: 0x00000001123fca53 UIKitCore`UIApplicationMain +

就会出现这样的顺序,可以发现延时函数确实是归Timer处理,但这不是重点,会走到Run函数,下层还有个RunSpecific,那直接复制粘贴

void CFRunLoopRun(void) {	/* DOES CALLOUT */
    int32_t result;
    do {
        result = CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
        CHECK_FOR_FORK();
    } while (kCFRunLoopRunStopped != result && kCFRunLoopRunFinished != result);
}

 run函数本质只是循环执行CFRunLoopRunSpecific,结束条件是RunLoop自然结束,或者手动停止

SInt32 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;
    //开始
	if (currentMode->_observerMask & kCFRunLoopEntry ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopEntry);
    //具体操作
	result = __CFRunLoopRun(rl, currentMode, seconds, returnAfterSourceHandled, previousMode);
    //结束   
	if (currentMode->_observerMask & kCFRunLoopExit ) __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);

    __CFRunLoopModeUnlock(currentMode);
    __CFRunLoopPopPerRunData(rl, previousPerRun);
	rl->_currentMode = previousMode;
    __CFRunLoopUnlock(rl);
    return result;
}

然后可以看到外部就是检测里面的状态进行操作的,先简单解释下状态

1.做一些常规操作,比如判断是否已经被释放,上锁情况,获取当前模式等等等等

2.调用了__CFRunLoopDoObservers处理了一些基础信息,另外变更RunLoop的状态

3.具体的操作在

static int32_t __CFRunLoopRun(CFRunLoopRef rl, CFRunLoopModeRef rlm, CFTimeInterval seconds, Boolean stopAfterHandle, CFRunLoopModeRef previousMode) 

(这个函数里面,实在是太长了,感兴趣的自行查看目录是CF->CFRunLoop.c的2331行开始,我这只是节选了具体的操作do-while的一部分代码)

int32_t retVal = 0;
do {
    //如果有就处理Timer
    __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeTimers);
    //如果有就处理block
    __CFRunLoopDoBlocks(rl, rlm);
    //如果有就处理Source0
        Boolean sourceHandledThisLoop = __CFRunLoopDoSources0(rl, rlm, stopAfterHandle);
        if (sourceHandledThisLoop) {
    //可能还会处理一次block(事件回调)
            __CFRunLoopDoBlocks(rl, rlm);
	}
    //处理source1,的线程问题
        if (__CFRunLoopServiceMachPort(dispatchPort, &msg, sizeof(msg_buffer), &livePort, 0, &voucherState, NULL)) {
            //如果有就跳转到handle_msg,就是处理线程之间的通讯消息
            goto handle_msg;
    }
    //如果处理完了设置状态进入休眠
	if (!poll && (rlm->_observerMask & kCFRunLoopBeforeWaiting))         
    __CFRunLoopDoObservers(rl, rlm, kCFRunLoopBeforeWaiting);
	__CFRunLoopSetSleeping(rl);

    //计算休眠时间
    CFAbsoluteTime sleepStart = poll ? 0.0 : CFAbsoluteTimeGetCurrent();
    //等待其它消息来唤醒        
    __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort, poll ? 0 : TIMEOUT_INFINITY, &voucherState, &voucherCopy);

    rl->_sleepTime += (poll ? 0.0 : (CFAbsoluteTimeGetCurrent() - sleepStart));

    // user callouts now OK again 用户唤醒了,现在继续搞起
	__CFRunLoopUnsetSleeping(rl);   //解除睡眠状态
	if (!poll && (rlm->_observerMask & kCFRunLoopAfterWaiting)) __CFRunLoopDoObservers(rl, rlm, kCFRunLoopAfterWaiting);
    //处理消息所以,其实RunLoop重新被唤醒是从处理消息开始的
    handle_msg:;
        __CFRunLoopSetIgnoreWakeUps(rl);
#if USE_DISPATCH_SOURCE_FOR_TIMERS //如果是被Timer唤醒
        else if (modeQueuePort != MACH_PORT_NULL && livePort == modeQueuePort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
#if USE_MK_TIMER_TOO.  //这里是被Timer唤醒TOO
        else if (rlm->_timerPort != MACH_PORT_NULL && livePort == rlm->_timerPort) {
            CFRUNLOOP_WAKEUP_FOR_TIMER();
            if (!__CFRunLoopDoTimers(rl, rlm, mach_absolute_time())) {
                // Re-arm the next timer
                __CFArmNextTimerInMode(rlm, rl);
            }
        }
#endif
        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 {
            CFRUNLOOP_WAKEUP_FOR_SOURCE();  //其实懂点英文都看的懂,被Source1唤醒
           //处理Source1
            CFRunLoopSourceRef rls = __CFRunLoopModeFindSourceForMachPort(rl, rlm, livePort);

     //处理block
	__CFRunLoopDoBlocks(rl, rlm);
    //设置返回值,是否要终止RunLoop否则重新把循环走一遍再休眠
	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);

所以可以得到结论,RunLoop的具体操作都是在__RunLoopRun这个函数执行,外层只是负责终止这个RunLoop

4.调用了__CFRunLoopDoObservers处理了即将退出的信息,注意这里只是退出当前这次循环,并不是退出了RunLoop,RunLoop退出必须是各种原因,比如线程终止了,RunLoop里面什么都没有,手动销毁等等情况造成的RunLoop销毁,另外变更RunLoop的状态

 官方图有点晦涩,这点还是MJ总结的到位点 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值