[IOS]关于RxSwift中的timer和flatMapLatest


我想做什么

我想使用RxSwift实现一个计时器。
由于我已经在产品中引入了RxSwift,因此我决定使用RxSwift的interval(_ period: RxTimeInterval, scheduler: SchedulerType)来测量时间,而不使用Timer类。

暂时实施

一个简单的示例

,通过打开或关闭UISwitch管理计时器"停止"和"启动"

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ViewController: UIViewController {

    @IBOutlet weak var timerLabel: UILabel!
    @IBOutlet weak var timerSwitch: UISwitch!

    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        timerSwitch.rx.value
            .flatMap { $0 ? Observable<Int>.interval(0.1, scheduler: MainScheduler.instance) : .empty() }
            .map { String($0) }
            .bind(to: timerLabel.rx.text)
            .disposed(by: disposeBag)
    }
}

截图
timer1.gif

結果
运作不正常。 .. ..
即使关闭开关,计时器也不会停止。两个计时器在第二个ON

处移动的行为

目前正在调试

在每个事件后放置debug()

1
2
3
4
5
6
7
timerSwitch.rx.value
    .debug("switch")
    .flatMap { $0 ? Observable<Int>.interval(0.1, scheduler: MainScheduler.instance) : .empty() }
    .debug("timer")
    .map { String($0) }
    .bind(to: timerLabel.rx.text)
    .disposed(by: disposeBag)

调试结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2019-04-12 11:59:02.794: switch -> subscribed
2019-04-12 11:59:02.795: switch -> Event next(false)
2019-04-12 11:59:04.193: switch -> Event next(true) <- Switch On
2019-04-12 11:59:04.293: timer -> Event next(0)
2019-04-12 11:59:04.394: timer -> Event next(1)
2019-04-12 11:59:04.494: timer -> Event next(2)
2019-04-12 11:59:04.593: timer -> Event next(3)
2019-04-12 11:59:04.642: switch -> Event next(false) <- Switch Off
2019-04-12 11:59:04.693: timer -> Event next(4)      <- あれ? 動いている
2019-04-12 11:59:04.794: timer -> Event next(5)
2019-04-12 11:59:04.893: timer -> Event next(6)
2019-04-12 11:59:04.994: timer -> Event next(7)
2019-04-12 11:59:05.248: switch -> Event next(true) <- Switch On
2019-04-12 11:59:05.293: timer -> Event next(8)
2019-04-12 11:59:05.349: timer -> Event next(0)      <- 2つ目のTimerが起動
2019-04-12 11:59:05.394: timer -> Event next(9)
2019-04-12 11:59:05.449: timer -> Event next(1)
2019-04-12 11:59:05.493: timer -> Event next(10)
2019-04-12 11:59:05.550: timer -> Event next(2)

flatMap中生成的Observable.interval不会被破坏,并会继续触发该事件。

关于flatMapflatMapLatest

flatMap

1
2
// Projects each element of an observable sequence to an observable sequence and merges the resulting observable sequences into one observable sequence.
public func flatMap<O>(_ selector: @escaping (Self.E) throws -> O) -> RxSwift.Observable<O.E> where O : ObservableConvertibleType

flatMap.c.png
创建一个新的Observable,并将生成的Observable合并为一个Observable。

在上面的大理石图中,红色事件生成了一个Observable,下一个绿色事件生成了一个Observable,红色和绿色Observable合并的Obsrvable事件正在流动。

flatMapLatest

1
2
// It is a combination of `map` + `switchLatest` operator
public func flatMapLatest<O>(_ selector: @escaping (Self.E) throws -> O) -> RxSwift.Observable<O.E> where O : ObservableConvertibleType

→只需放入map switchLatest

如果

,那么switchLatest怎么办?

switch.c.png
每次收到新的内部可观察对象时,您都将取消订阅先前的内部可观察对象,并订阅最新的内部可观察对象。

flatMapflatMapLatest有什么区别?

mergeswitchLatest之间的区别。

flatMapmerge新生成的Observable,而flatMapLatestswitchLatest
因此,在flatMap的情况下,事件生成的Observable为merge,因此除非生成的Observable完成,否则它将增加,但是在创建新的Observable时flatMapLatest将与先前的Observable相同。 。它不会增加,因为取消了订阅。

这次的计时器数量增加了,因为使用了flatMap,计时器的可观察项又一次合并了。

改善

我将

flatMap的部分更改为flatMapLatest,它可以安全地工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class ViewController: UIViewController {

    @IBOutlet weak var timerLabel: UILabel!
    @IBOutlet weak var timerSwitch: UISwitch!

    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        timerSwitch.rx.value
            .flatMapLatest { $0 ? Observable<Int>.interval(0.1, scheduler: MainScheduler.instance) : .empty() }
            .map { String($0) }
            .bind(to: timerLabel.rx.text)
            .disposed(by: disposeBag)
    }
}

摘要

  • flatMapLatest的其他用法用于禁止增量搜索等中的UI更新。
  • 实例管理比Observable.interval()は、 Timer更容易使用。
  • 了解flatMapflatMapLatestswitchLatestmerge

参考

  • github --RxSwift
  • reactX --flatmap
  • reactX-开关