探索 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的主要内容,如果未能解决你的问题,请参考以下文章

R等同于SAS do-loop

iOS多线程开发---Run Loop(二,三)

iOS多线程开发---Run Loop

为啥大多数 asyncio 示例都使用 loop.run_until_complete()?

用 asyncio.run 替换 asyncio.get_event_loop().run_until_complete

如何用 ProcessPoolExecutor 优雅地终止 loop.run_in_executor?