探索 Run Loop
Posted chenxianming
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了探索 Run Loop相关的知识,希望对你有一定的参考价值。
说到RunLoop,无论从项目代码或者网上都经常会看到以下这段代码:
while (!_isFinish)
{
NSRunLoop *runloop = [NSRunLoop currentRunLoop];
[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
究竟[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]干啥的?
本文最重要的是要说明白这个事情!准备好开始了.(如果对这部分探索过程看不下去可直接跳到第二部分,第二部分是根据第一部分的结果写出来的demo).
对于single view application的应用程序,UIApplicationMain()方法在这里不仅完成了初始化我们的程序并设置程序Delegate的任务,而且随之开启了主线程的Run Loop, 开始接受处理事件。
从简单开始,通过监听runloop状态,查看进入程序什么都不干,runloop会干点啥。
监听代码如下:
runLoopActivityStringWithActivity函数只是简单的把值转换成对应的文本方便查看。
打印结果如下:
log出来CFRunLoopActivity对应着以下枚举看。
typedef CF_OPTIONS(CFOptionFlags, CFRunLoopActivity) {
kCFRunLoopEntry = (1UL << 0),
// 即将进入Loop
kCFRunLoopBeforeTimers = (1UL << 1),
// 即将处理 Timer
kCFRunLoopBeforeSources = (1UL << 2),
// 即将处理 Source
kCFRunLoopBeforeWaiting = (1UL << 5),
// 即将进入休眠
kCFRunLoopAfterWaiting = (1UL << 6),
// 刚从休眠中唤醒
kCFRunLoopExit = (1UL << 7),
// 即将退出Loop
};
可知道:程序在经过了一系列变化后进入了kCFRunLoopBeforeWaiting休眠状态。
然后在viewController里加入个按钮button one,响应代码如下:
点击后打印结果:
这时候可以看one button tap begin 往前三行,输出了kCFRunLoopAfterWaiting
主线程被换醒,然后处理按钮事件,接着过了一阵子就会从新进入休眠,等待下一事件。
继续往下,我又加了一个button two。并把代码修改成如下,大家都知道,点击了
button one 后就进入了死循环,点击button two 是不起作用的。这里就不上Log了。
但是把button one 代码改成下面这样,就算button one代码没有执行完,同样可以
在主线程响应button two的事件。究竟
[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
做了什么事情呢?
此时打印出来的log如下:
从log中看到并没有打印出one button End.说明one button 还没有执行完。
但是我们可以看到one button before和after RunLoop被打印了好几次,并且CFRunLoopActivity
打印出来好几次kCFRunLoopEntry和kCFRunLoopExit.说明RunLoop是有退出和重新进入的。不是说主线程的runLoop不会退出的吗?
难道打出来的不是主线程的runLoop?下面就慢慢说明白这些东西
为了方便理解,我会把启动时应用给我们创建的RunLoop叫做RunLoop对象,可以这样说RunLoop对象调用runMode创建了一个循环,这个循环可以在有事件发生里处理
事件,没事发生就会休眠,等待事件发生。那为什么开始没改代码时那种情况为什么响应不了button two的事件呢,那是因为处理button one事件没还返回给系统,系统给我们创建的这个循环并不能往下走了,所以在系统创建的循环处理不了button two。而修改代码后,[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]其实是为RunLoop对象创建了个新的循环。创建一个循环只会有一个kCFRunLoopEntry和kCFRunLoopExit,中间的CFRunLoopActivity状态则要看情况而定,如有哪个输入源等。到达kCFRunLoopExit表明这个循环要退出。由于while中一直为true,由我们代码创循环会不停被启动。[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]创建的循环我叫它子循环,button two 事件其实是在这个子循环里被处理。所以RunLoop对象的循环可以有多个,并且是嵌套的。事件处理会被嵌套最深的那个子循环处理。为了证实这个说法继续加了个button three,并把button two 代码修改,代码如下
连续点击button one two there.在three中打个断点。调用栈如下:
很明显看到,button two的事件是在one中创建的子循环执行,而button three的事件是在two中创建的子
循环执行。
总结:
1、[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]其实是为线程创建
一个循环,如果线程已经有,创建的是一个子循环。
2、通过[runloop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]创建的循环过一段时间或
马上返回,这取决于输入源及系统的调度。所以用while进行不断驱动,不停创建循环。
3、我们看到日志打印出kCFRunLoopExit是子循环的exit.
以上是关于探索 Run Loop的主要内容,如果未能解决你的问题,请参考以下文章
为啥大多数 asyncio 示例都使用 loop.run_until_complete()?
用 asyncio.run 替换 asyncio.get_event_loop().run_until_complete