RxSwift学习-10-Scheduler调度之主线程调度

675 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情

  • 本文主要介绍Rx Swift中的调度者,类似我们开发中的线程,关于Scheduler主要是对我们GCD得封装

1. 线程和队列

这里说明下GCD下进程,线程,队列,函数的关系。

  • 进程:我们程序执行的基本单位
  • 线程:用来执行我们任务的单位,其中主要分为主线程(通常main函数启动后自动开启,也称UI线程,主要调用我们UI的API的线程),子线程(通常我们处理一些耗时操作,或者数据处理等)。线程通常使用runloop进行管理,分配资源。
  • 队列:管理我们任务的单位,遵循FIFO先进先出的原则。分为主队列全局队列串行队列并发队列。本质上就是串行并发
  • 函数:通常就是异步函数同步函数,函数依赖于队列,函数可以认为是我们执行任务的单位,通常在GCD函数指定队列
  • 队列和线程的关系:一个线程可以有多个队列,当我们比如开启并发队列异步函数的时候,不一定会开启新的线程,取决于当前线程的状态。 具体情况可以看下我之前GCD的分析

2 UI案例

我们知道通常UI的API要在主线程进行更新或者操作,如果在子线程操作则会出现下图所示

image.png

因此对于UI操作我们通常是子线程异步函数中获取数据后,在主线程中进行刷新数据。比如我们在子线程操作就没问题

image.png

对于RxSwift我们操作并没有关注线程相关操作,那么它是如何做的,可以发现UI的操作在主线程

image.png

点击查看tap,进入controlEvent事件中的touchUpInside,点击查看controlEvent

image.png

在创建序列事件会先调用MainScheduler.ensureRunningOnMainThread()从字面意义应该是确保当前运行的代码在主线程。我们顺便讲下rx绑定UI事件的原理:

  • ControlTarget

image.png

在初始化的时候把添加一个中间层用于接受系统的点击事件类型,之后会响应我们中间层的方法进行回调

image.png

通过callback把我们系统的control传到外面的订阅

image.png

销毁的时候进行移除相关操作,总结下来就是通过一个中间层系统的事件转换为rx的可观察序列进行订阅。

3. MainScheduler

MainScheduler类似我们GCD的主队列

image.png

可以发现对于MainScheduler的初始化就是对DispatchQueue.main的封装,其中instance便是实例化,asyncInstance表示主队列的异步函数

  • SerialDispatchQueueScheduler MainScheduler继承于SerialDispatchQueueScheduler相当于GCD的串步队列

image.png

提供了一些便利构造方法,这个调度器也可以单独用于内部串行队列。如果在使用之前需要对它进行一些定制,内部串行队列可以使用serialQueueConfiguration来定制回调。其中leeway表示延时时间,不传默认0.

通过上面我们可以知道我们的UI事件是绑定的主线程上进行调度的,那么如何知道它是主队列还是并发队列的。比如我们打印Thread.current可以知道线程。

  • CurrentThreadScheduler

在RxSwift中通过CurrentThreadScheduler进行管理,通过调度当前线程进行工作

image.png isScheduleRequiredKey表示的当前线程的key,用于标记线程的。

image.png线程进行拓展,线程里有个字典可以保存key,我们通过

image.png

我们关注下isScheduleRequired

image.png

是获取当前线程和我们要调度的线程是否是同一个线程不是的话需要调用schedule方法。这个方法在我们最开始Producer中的subscribe 中有使用

image.png

在进行订阅的时候先判断当前线程是否需要调度,不需要的话直接执行,当前的线程和我们设定的不是同一个则需要进行调度

image.png

调度的时候CurrentThreadScheduler.isScheduleRequired = false这样下次就不会进入,其中defer关键字表示再 return后执行为空,序列销毁后,清除当前调度器信息。 闭包回调action(state)会执行外部的闭包

4.UI调用原理

我们上面知道我们异步函数是子线程按钮点击主线程。看下点击事件

image.png

点击进入可以发现我们点击序列 发送在主队列上执行了: subscribe(on: ConcurrentMainScheduler.instance)

  • SubscribeOn

点击进入可以发现自己对ObservableType协议进行了拓展,增加了调度器

image.png

点击进入发现SubscribeOn这个类,它继承Producer,同时它的元素遵循ObservableType协议,类似我们之前可观察序列Observable

image.png

因此在订阅的时候会执行下面的run方法,这个方法和我们之前探究的类似,我们去找管子的run方法

image.png

此时我们的parent是我们之前SubscribeOn序列,里面保存了我们scheduler也就是我们外面传进来的ConcurrentMainScheduler。继续调用ConcurrentMainScheduler的schedule方法

image.png

调用

image.png

此时我们的操作回调必然在主线程异步函数中了。之后调用subscribe,把管子的信息传递出去

image.png

这里的source是我们初始化SubscribeOn传的Observable<Void>也就是我们点击的序列,至此我们有了大概的流程。

未命名文件-6.jpg

5. 总结

以上就是关于主线程异步函数调用同步函数类似。对于UI事件是封装在主线程的异步函数中执行因此,我们不用担心我们使用RxSwift中线程问题。后面分析并发队列异步函数,未完待续。