RunLoop 二 runLoopMode 的四种消息

一、回答问题

  1. runLoop切换 Mode 时,程序是否会退出?
    答:不会退出。
    解释:切换模式不会导致程序退出。

刚才 CFRunLoopModeRef 中的定义:“如果需要切换Mode,只能退出当前Loop,在重新选择一个Mode进入。”这句话是说:“退出当前循环的代码,重新进入”。 相当于:

  • 切换 mode 是在 这两句代码里面做的。代码为:
  • 在这两句中切换mode
  • 切换mode 并不会导致 while (0 == retVal) 这句循环的结果发生变化。所以不会导致程序退出。

二、RunLoop 模式

  1. runLoop 会选择一种模式运行。当它选择了一种模式运行后,它会执行 上面代码 的 sleep_and_wait() 方法。有消息就会执行。
  2. 它会执行什么消息? 它会处理 Sources0,Sources1, observers, timers.
    sleep_and_wait()方法所要执行的4个消息

三、RunLoop 模式中所要处理的4种消息

  1. Source0
  • 触摸事件处理
  • performSelector:onThread:
  • touchesBegan:withEvent:

例如:- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event 方法

  • 这个方法就是通过 runLoop 进行处理的,因为点击事件就是通过 runLoop处理的。

  • 运行程序,在 NSLog这句代码上 打断点。
    touchesBegan方法

  • 再次运行程序,可以看到左边的程序栈,但是系统隐藏了一些。只能看到1,14,15,16. 没有 2 - 13 。
    touchesBegan 方法的程序栈,只能看到一小部分 2-13看不见

  • 可以通过 lldb 指令 bt 就可以打印函数调用栈。就可以看到了 1 - 15 全部的方法。 打印结果为:
     lldb 指令 bt 就可以打印全部的函数调用栈

  • 可以看到里面第8个 是 frame #8: 0x0000000106453101 CoreFoundation__CFRUNLOOP_IS _CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17

  • 这句话里面就可以看到 由 source0 出发 调用 第7个 __handleEventQueueInternal 处理这个事件,在调用到 touchesBegin 这个方法

  • 所以:处理点击事件,是由 Source0 处理的

另外: performSelector:onThread: 方法也是由 Source0 处理的。

例如:
[self performSelector:<#(nonnull SEL)#> onThread:<#(nonnull NSThread *)#> withObject:<#(nullable id)#> waitUntilDone:<#(BOOL)#>]

  • 传入一个线程对象给onThread参数,performSelector参数里传入的方法 可以放入到 onThread 参数 的线程中去执行。
  • 比如说给 onThread 参数一个子线程,就可以让 performSelector参数里传入的方法 在子线程中执行。
  1. Source1
  • 基于Port的线程间通信

  • 系统事件捕捉

  • 基于Port的线程间通信

    • 可以理解成,基于 接口进行通讯。比如说: 线程1 有一个接口,线程2 有一个接口,那么只能通过 这两个接口进行通讯。
      基于port的线程间通信
  • 系统事件捕捉

    • 这个也是 Source1 来管理。
    • 例如:在屏幕上任意地方点击了一下,所产生的点击事件。是通过 Source1 去捕捉 点击事件,再包装成 Source0 去处理。
    • 相当于事件是通过 Source1 去捕捉,在分发到 Source0 去处理。
  1. Timers
  • NSTimer
  • performSelector:withObject:afterDelay:(属于 Timers 里面的)

例如:[self performSelector:@"方法名称" withObject:<#(nullable id)#> afterDelay:2]

  • 两秒后执行 方法。
  • 这句代码的底层就是 NSTimer,就是定时器。
  • 定时器 属于 timers 的范畴
  1. Observers
  • 用于监听RunLoop的状态
  • UI刷新(BeforeWaiting)
  • Autorelease pool(BeforeWaiting)

UI刷新:

  • 它就是通过监听 RunLoop 的状态 来改变的。

  • 比如:监听到 RunLoop 要睡觉的话,就会在 RunLoop 睡觉之前刷新UI界面。

  • 例如:self.view.backgroundColor = [UIColor redColor] 这句代码。它不是写完后立即生效,而是执行到这句代码时先记住有这个事情,在什么时候这句代码会生效?这句代码如何会生效呢?

  • 这句代码如何会生效呢?

    • 通过 Observers 让这句代码生效。Observers 一旦监听到 RunLoop即将睡觉,会在RunLoop的线程 睡觉之前刷新UI界面。这个时候 这句代码就会生效。

Autorelease pool

  • 通过 Observers 来实现。
  • 会在RunLoop的线程 睡觉之前,清空下释放池

四、RunLoop 的运行逻辑
01、通知Observers:进入Loop
02、通知Observers:即将处理Timers
03、通知Observers:即将处理Sources
04、处理Blocks
05、处理Source0(可能会再次处理Blocks)
06、如果存在Source1,就跳转到第8步
07、通知Observers:开始休眠(等待消息唤醒)
08、通知Observers:结束休眠(被某个消息唤醒)
01>处理Timer
02> 处理GCD Async To Main Queue
03> 处理Source1
09、处理Blocks
10、根据前面的执行结果,决定如何操作
01> 回到第02步
02> 退出Loop
11、通知Observers:退出Loop

猜你喜欢

转载自blog.csdn.net/M316625387/article/details/83113796
今日推荐