首页 > 解决方案 > 防止 RxSwift 中的冗余操作

问题描述

我正在用 RxSwift 开始我的冒险,在 js 中使用 React 已经有了一些经验。我认为我的问题很常见,但我不确定如何以简洁抽象的方式描述它,所以我将在示例中描述它。

我正在构建显示一些图表的 iOS 应用程序。感兴趣的部分包括 ChartAreaController、ChartInfoController,它们都嵌入在 ChartController 中。第一个控制器是显示一些图形的区域(基于 rx chartData 属性),第二个控制器将有一个滑块供用户限制显示 x 值(rx selectedXRange 属性),该值被限制在某个最小值和最大值之间。最小值/最大值由当前图表数据定义。

在 ChartController 中定义滑块更改更新图表时的行为:

override func viewDidLoad() {
   super.viewDidLoad()
   (...)
   chartInfoController.selectedXRange.asObservable()
                .subscribe(onNext: { [unowned self] selectedXRange in
                    (...)
                    let chartData = self.filterChartData(from: self.rawChartData, in: selectedXRange)
                    self.chartAreaController.chartData.accept(chartData)
                }).disposed(by: disposeBag)

filterChartData() 方法只是过滤掉不在范围内的数据,但为了论证的目的,我们可以假设它非常昂贵,我不希望它在不需要时运行两次。

当用户更改他或她想要显示的图表时,新数据从服务器到达(同样是 ChartController):

private func handleNewData(_ rawChartData: ChartData) {
        self.rawChartData = rawChartData

        guard let allowedXRange = rawChartData.xRange() else { return }
        let selectedXRange = chartInfoController.selectedXRange.value
        let newSelectedXRange = calculateSelectedXRange(currentSelectedDays: selectedDaysRange, availableDaysRange: daysRange)
        let chartData = filterChartData(from: rawChartData, in: selectedXRange)

        self.chartInfoController.allowedXRange = allowedXRange //this line is not crucial
        self.chartInfoController.selectedXRange.accept(newSelectedXRange)

        self.chartAreaController.chartData.accept(rawChartData)
    }

因此,在新图表数据到达时,由于数据的新最小值/最大值,可能必须修剪当前选择的 xRange。因此,该方法的副作用将是更改 selectedXRange 并运行我之前粘贴的订阅。因此,当新数据到达时,chartData 会更新两次,我不希望它发生。

当然我可以注释掉handleNewData()方法的最后一行,但我不是很喜欢它,因为handleNewData()存在的主要原因是设置chartData,并且注释掉的行的目标是由于该方法的副作用(即更新滑块)而实现。不能接受的。

无论如何,我对 chartData 添加了油门,因为快速移动的滑块会导致许多更新,这部分解决了我的问题(chartData 只更新了一次)。但是您可能还记得 filterChartData() 方法的成本很高,而且这部分仍然会运行两次。

所以一个问题是,如果我解决问题的总体布局是可以的,还是应该以不同的方式处理?在这一点上,我得出的结论是,我正在寻找某种方法来临时禁用 selectedXRange 上的特定订阅(而不破坏对该变量的其他订阅)。暂时的意思:

(...)
//disable subscription
self.chartInfoController.selectedXRange.accept(newSelectedXRange)
self.chartAreaController.chartData.accept(rawChartData)
//enable subscription
(...)

这对我来说似乎是合法的,因为 ChartController 作为订阅的所有者和值的更改者可能希望在适合他的时候禁用订阅(它?)。

RxSwift 是否支持这样的东西?如果没有,那么我想我可以自己实现它,例如通过 ChartController 中的 bool 属性,或者通过将订阅添加到单独的 disposeBag,我将处理然后重新创建订阅。但如果这样做是好事呢?例如,当出现一些错误时,bool 解决方案可能容易处理不当,并且 dispose/recreate 可能会以某种方式代价高昂,并且可能不打算像这样使用 dispose。

有没有更好的做法来处理这种情况?正如我所说,我认为这个问题很常见,所以我希望有一个规范的解决方案:) 感谢您的任何回答,对于冗长的帖子感到抱歉。

标签: iosswiftreactive-programmingrx-swiftrx-cocoa

解决方案


所以一个问题是,如果我解决问题的总体布局是可以的,还是应该以不同的方式处理?

正确编写的 UI 输入元素 observable 只会在用户对 UI 进行更改时触发,而不是在程序进行更改时触发。例如: textField.rx.text.orEmpty.subscribe(onNext: { print($0) })只会在用户输入 textField 时打印一个值,而不是在您调用textField.text = "foo"或从绑定时打印.bind(to: textfield.rx.text)

如果您编写了 ChartInfoController,我建议您修改它以使其与其他 UI 元素一样工作。如果您没有编写它,请将问题提交给开发人员/维护人员。

RxSwift 是否支持 [暂时禁用特定订阅] 之类的功能?

这取决于您所说的“暂时禁用”是什么意思。它不支持静默取消订阅和重新订阅,但是有很多运算符会过滤掉他们收到的一些事件,同时传递其他事件。例如filter, throttle, debounce, ignoreElements... 有很多人这样做。

有没有更好的做法来处理这种情况?

然后上面提到了最佳解决方案。


推荐阅读