一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第12天,点击查看活动详情。
- 本文主要介绍Rx Swift中的调度者,类似我们开发中的线程,关于
Scheduler主要是对我们GCD得封装
1. 线程和队列
这里说明下GCD下进程,线程,队列,函数的关系。
- 进程:我们
程序执行的基本单位 - 线程:用来执行我们
任务的单位,其中主要分为主线程(通常main函数启动后自动开启,也称UI线程,主要调用我们UI的API的线程),子线程(通常我们处理一些耗时操作,或者数据处理等)。线程通常使用runloop进行管理,分配资源。 - 队列:
管理我们任务的单位,遵循FIFO先进先出的原则。分为主队列,全局队列,串行队列,并发队列。本质上就是串行和并发。 - 函数:通常就是
异步函数和同步函数,函数依赖于队列,函数可以认为是我们执行任务的单位,通常在GCD函数要指定队列。 - 队列和线程的关系:一个线程可以有多个队列,当我们比如开启
并发队列异步函数的时候,不一定会开启新的线程,取决于当前线程的状态。 具体情况可以看下我之前GCD的分析
2 UI案例
我们知道通常UI的API要在主线程进行更新或者操作,如果在子线程操作则会出现下图所示
因此对于UI操作我们通常是子线程异步函数中获取数据后,在主线程中进行刷新数据。比如我们在子线程操作就没问题
对于RxSwift我们操作并没有关注线程相关操作,那么它是如何做的,可以发现UI的操作在主线程
点击查看tap,进入controlEvent事件中的touchUpInside,点击查看controlEvent
在创建序列事件会先调用MainScheduler.ensureRunningOnMainThread()从字面意义应该是确保当前运行的代码在主线程。我们顺便讲下rx绑定UI事件的原理:
ControlTarget
在初始化的时候把添加一个中间层用于接受系统的点击事件类型,之后会响应我们中间层的方法进行回调
通过callback把我们系统的control传到外面的订阅中
销毁的时候进行移除相关操作,总结下来就是通过一个中间层把系统的事件转换为rx的可观察序列进行订阅。
3. MainScheduler
MainScheduler类似我们GCD的主队列
可以发现对于MainScheduler的初始化就是对DispatchQueue.main的封装,其中instance便是实例化,asyncInstance表示主队列的异步函数。
SerialDispatchQueueSchedulerMainScheduler继承于SerialDispatchQueueScheduler相当于GCD的串步队列
提供了一些便利构造方法,这个调度器也可以单独用于内部串行队列。如果在使用之前需要对它进行一些定制,内部串行队列可以使用serialQueueConfiguration来定制回调。其中leeway表示延时时间,不传默认0.
通过上面我们可以知道我们的UI事件是绑定的主线程上进行调度的,那么如何知道它是主队列还是并发队列的。比如我们打印Thread.current可以知道线程。
CurrentThreadScheduler
在RxSwift中通过CurrentThreadScheduler进行管理,通过调度当前线程进行工作
isScheduleRequiredKey表示的当前线程的key,用于标记线程的。
对
线程进行拓展,线程里有个字典可以保存key,我们通过
我们关注下isScheduleRequired
是获取当前线程和我们要调度的线程是否是同一个线程,不是的话需要调用schedule方法。这个方法在我们最开始Producer中的subscribe 中有使用
在进行订阅的时候会先判断当前线程是否需要调度,不需要的话直接执行,当前的线程和我们设定的不是同一个则需要进行调度。
调度的时候CurrentThreadScheduler.isScheduleRequired = false这样下次就不会进入,其中defer关键字表示再 return后再执行为空,序列销毁后,清除当前调度器信息。
闭包回调action(state)会执行外部的闭包
4.UI调用原理
我们上面知道我们异步函数是子线程,按钮点击在主线程。看下点击事件
点击进入可以发现我们点击序列 发送在主队列上执行了:
subscribe(on: ConcurrentMainScheduler.instance)
SubscribeOn
点击进入可以发现自己对ObservableType协议进行了拓展,增加了调度器。
点击进入发现SubscribeOn这个类,它继承Producer,同时它的元素遵循ObservableType协议,类似我们之前可观察序列Observable。
因此在订阅的时候会执行下面的run方法,这个方法和我们之前探究的类似,我们去找管子的run方法
此时我们的parent是我们之前SubscribeOn序列,里面保存了我们scheduler也就是我们外面传进来的ConcurrentMainScheduler。继续调用ConcurrentMainScheduler的schedule方法
调用
此时我们的操作回调必然在主线程异步函数中了。之后调用subscribe,把管子的信息传递出去
这里的source是我们初始化SubscribeOn传的Observable<Void>也就是我们点击的序列,至此我们有了大概的流程。
5. 总结
以上就是关于主线程的异步函数调用,同步函数类似。对于UI事件是封装在主线程的异步函数中执行因此,我们不用担心我们使用RxSwift中线程问题。后面分析并发队列异步函数,未完待续。