纯净、安全、绿色的下载网站

首页|软件分类|下载排行|最新软件|IT学院

当前位置:首页IT学院IT技术

IOS RunLoop的事件循环机制 分析IOS RunLoop的事件循环机制

为童沉沦   2021-06-07 我要评论
想了解分析IOS RunLoop的事件循环机制的相关内容吗为童沉沦在本文为您仔细讲解IOS RunLoop的事件循环机制的相关知识和一些Code实例欢迎阅读和指正我们先划重点:IOS,RunLoop下面大家一起来学习吧

在RunLoop启动之后会发送一个通知来告知观察者

将要处理Timer/Source0事件这样一个通知的发送

处理Source0事件

如果有Source1要处理这时会通过一个go to语句的实现来进行代码逻辑的跳转处理唤醒是收到的消息

如果没有Source1要处理线程就将要休眠同时发送一个通知告诉观察者

然后线程进入一个用户态到内核态的切换休眠然后等待唤醒唤醒的条件大约包括三种:

1、Source1

2、Timer事件

3、外部手动唤醒

线程刚被唤醒之后也要发送一个通知告诉观察者然后处理唤醒时收到的消息

回到将要处理Timer/Source0事件这样一个通知的发送

然后再次进行上面步骤这就是一个RunLoop的事件循环机制

内部代码逻辑整理如下:

/// 用DefaultMode启动
void CFRunLoopRun(void) {
    CFRunLoopRunSpecific(CFRunLoopGetCurrent(), kCFRunLoopDefaultMode, 1.0e10, false);
}
 
/// 用指定的Mode启动允许设置RunLoop超时时间
int CFRunLoopRunInMode(CFStringRef modeName, CFTimeInterval seconds, Boolean stopAfterHandle) {
    return CFRunLoopRunSpecific(CFRunLoopGetCurrent(), modeName, seconds, returnAfterSourceHandled);
}
 
/// RunLoop的实现
int CFRunLoopRunSpecific(runloop, modeName, seconds, stopAfterHandle) {
    
    /// 首先根据modeName找到对应mode
    CFRunLoopModeRef currentMode = __CFRunLoopFindMode(runloop, modeName, false);
    /// 如果mode里没有source/timer/observer, 直接返回
    if (__CFRunLoopModeIsEmpty(currentMode)) return;
    
    /// 1. 通知 Observers: RunLoop 即将进入 loop
    __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopEntry);
    
    /// 内部函数进入loop
    __CFRunLoopRun(runloop, currentMode, seconds, returnAfterSourceHandled) {
        
        Boolean sourceHandledThisLoop = NO;
        int retVal = 0;
        do {
 
            /// 2. 通知 Observers: RunLoop 即将触发 Timer 回调
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeTimers);
            /// 3. 通知 Observers: RunLoop 即将触发 Source0 (非port) 回调
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeSources);
            /// 执行被加入的block
            __CFRunLoopDoBlocks(runloop, currentMode);
            
            /// 4. RunLoop 触发 Source0 (非port) 回调
            sourceHandledThisLoop = __CFRunLoopDoSources0(runloop, currentMode, stopAfterHandle);
            /// 执行被加入的block
            __CFRunLoopDoBlocks(runloop, currentMode);
 
            /// 5. 如果有 Source1 (基于port) 处于 ready 状态直接处理这个 Source1 然后跳转去处理消息
            if (__Source0DidDispatchPortLastTime) {
                Boolean hasMsg = __CFRunLoopServiceMachPort(dispatchPort, &msg)
                if (hasMsg) goto handle_msg;
            }
            
            /// 通知 Observers: RunLoop 的线程即将进入休眠(sleep)
            if (!sourceHandledThisLoop) {
                __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopBeforeWaiting);
            }
            
            /// 7. 调用 mach_msg 等待接受 mach_port 的消息线程将进入休眠, 直到被下面某一个事件唤醒
            /// • 一个基于 port 的Source 的事件
            /// • 一个 Timer 到时间了
            /// • RunLoop 自身的超时时间到了
            /// • 被其他什么调用者手动唤醒
            __CFRunLoopServiceMachPort(waitSet, &msg, sizeof(msg_buffer), &livePort) {
                mach_msg(msg, MACH_RCV_MSG, port); // thread wait for receive msg
            }
 
            /// 8. 通知 Observers: RunLoop 的线程刚刚被唤醒了
            __CFRunLoopDoObservers(runloop, currentMode, kCFRunLoopAfterWaiting);
            
            /// 收到消息处理消息
            handle_msg:
 
            /// 9.1 如果一个 Timer 到时间了触发这个Timer的回调
            if (msg_is_timer) {
                __CFRunLoopDoTimers(runloop, currentMode, mach_absolute_time())
            } 
 
            /// 9.2 如果有dispatch到main_queue的block执行block
            else if (msg_is_dispatch) {
                __CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__(msg);
            } 
 
            /// 9.3 如果一个 Source1 (基于port) 发出事件了处理这个事件
            else {
                CFRunLoopSourceRef source1 = __CFRunLoopModeFindSourceForMachPort(runloop, currentMode, livePort);
                sourceHandledThisLoop = __CFRunLoopDoSource1(runloop, currentMode, source1, msg);
                if (sourceHandledThisLoop) {
                    mach_msg(reply, MACH_SEND_MSG, reply);
                }
            }
            
            /// 执行加入到Loop的block
            __CFRunLoopDoBlocks(runloop, currentMode);
            
 
            if (sourceHandledThisLoop && stopAfterHandle) {
                /// 进入loop时参数说处理完事件就返回
                retVal = kCFRunLoopRunHandledSource;
            } else if (timeout) {
                /// 超出传入参数标记的超时时间了
                retVal = kCFRunLoopRunTimedOut;
            } else if (__CFRunLoopIsStopped(runloop)) {
                /// 被外部调用者强制停止了
                retVal = kCFRunLoopRunStopped;
            } else if (__CFRunLoopModeIsEmpty(runloop, currentMode)) {
                /// source/timer/observer一个都没有了
                retVal = kCFRunLoopRunFinished;
            }
            
            /// 如果没超时mode里没空loop也没被停止那继续loop
        } while (retVal == 0);
    }
    
    /// 10. 通知 Observers: RunLoop 即将退出
    __CFRunLoopDoObservers(rl, currentMode, kCFRunLoopExit);
}

可以看到实际上 RunLoop 就是这样一个函数其内部是一个do-while循环当你调用CFRunLoopRun()时线程就会一直停留在这个循环里直到超时或被手动停止该函数才会返回

有一个这样的问题:当我们点击一个app从我们点击到程序启动、程序运行再到程序杀死这个过程系统都发生了什么呢?

实际上当我们调用了main函数之后会调用UIApplicationMain函数在这个函数内部会启动主线程的RunLoop然后经过一系列的处理最终主线程的RunLoop会处于一个休眠状态然后我们此时如果点击一下屏幕会转化成一个Source1来让我们的主线程唤醒然后当我们杀死程序时会调用RunLoop的退出同时发送通知告诉观察者

找到一张总结图帮助记忆:


相关文章

猜您喜欢

  • Vue顶部搜索栏 Vue+Vant实现顶部搜索栏

    想了解Vue+Vant实现顶部搜索栏的相关内容吗在奋斗的大道在本文为您仔细讲解Vue顶部搜索栏的相关知识和一些Code实例欢迎阅读和指正我们先划重点:Vue,Vant,搜索栏下面大家一起来学习吧..
  • Java求解二叉树 Java求解二叉树的最近公共祖先实例代码

    想了解Java求解二叉树的最近公共祖先实例代码的相关内容吗南淮北安在本文为您仔细讲解Java求解二叉树的相关知识和一些Code实例欢迎阅读和指正我们先划重点:java实现二叉树,java二叉树算法,java求二叉树的高度下面大家一起来学习吧..

网友评论

Copyright 2020 www.fresh-weather.com 【世纪下载站】 版权所有 软件发布

声明:所有软件和文章来自软件开发商或者作者 如有异议 请与本站联系 点此查看联系方式