Запретить избыточные операции в RxSwift - PullRequest
2 голосов
/ 30 апреля 2020

Я начинаю свое приключение с RxSwift, уже имея небольшой опыт работы с React в js. Я думаю, что моя проблема распространена, но я не уверен, как описать ее в краткой абстрактной форме, поэтому вместо этого я опишу ее на примере.

Я создаю 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 должен быть обрезан из-за новых значений min / max данных. Таким образом, побочным эффектом метода будет изменение selectedXRange и, в свою очередь, запуск подписки, которую я вставил ранее. Поэтому, когда приходят новые данные, chartData обновляется дважды, и я не хочу, чтобы это произошло.

Конечно, я могу закомментировать последнюю строку метода handleNewData (), но мне это не очень нравится , поскольку основной причиной существования handleNewData () является установка chartData, а с закомментированной строкой его цель будет достигнута из-за побочного эффекта метода (который обновляет ползунок). Неприемлемо.

В chartData я все равно добавил газ, потому что быстро движущийся ползунок приведет ко многим обновлениям, и это частично решит мою проблему (chartData обновляется только один раз). Но, как вы, возможно, помните, метод filterChartData () является дорогостоящим, и эта часть будет по-прежнему выполняться дважды.

Таким образом, вопрос в том, в порядке ли мой общий план решения проблемы, или он должен быть обработан иначе? На этом этапе я пришел к выводу, что я ищу способ временного отключения определенной подписки на selectedXRange (без повреждения других подписок на эту переменную). Временное значение:

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

Мне кажется, это git, поскольку ChartController как владелец подписки и средство изменения значений может захотеть отключить подписку, когда ему это удобно (не так ли?).

Поддерживает ли RxSwift что-то подобное? Если нет, то я думаю, что смогу достичь этого сам, например, через свойство bool в ChartController или добавив подписку в отдельный disposeBag, который я бы удалил, а затем пересоздал бы подписку. Но хорошо ли это делать? Например, решение bool может быть подвержено плохому обращению при возникновении какой-либо ошибки, а утилизация / воссоздание может быть как-то дорогостоящим, и это может быть случай, когда утилизация не предназначалась для такого использования.

Есть ли лучшая практика для обработки таких ситуаций? Как я уже сказал, я думаю, что проблема является общей, поэтому я надеюсь, что есть каноническое решение :) Спасибо за любой ответ, извините за длинный пост.

Ответы [ 2 ]

0 голосов
/ 04 мая 2020

Когда у нас есть несколько подписок на одну и ту же Наблюдаемую, она будет повторно выполняться для каждой подписки. Чтобы остановить повторное выполнение для каждой подписки. RxSwift имеет несколько операторов для этого: share(), replay(), replayAll(), shareReplay(), publish() и даже shareReplayLatestWhileConnected().

подробнее на ( RxSwift: поделиться против воспроизведение против shareReplay )

0 голосов
/ 01 мая 2020

Таким образом, один вопрос, если мой общий план решения проблемы в порядке, или он должен быть обработан иначе?

Правильно написанный элемент ввода пользовательского интерфейса, наблюдаемый, будет запускаться только когда пользователь вносит изменения в пользовательский интерфейс, а не когда программа вносит изменения. Например: textField.rx.text.orEmpty.subscribe(onNext: { print($0) }) будет печатать значение только тогда, когда пользователь вводит текстовое поле, а не когда вы вызываете textField.text = "foo" или из привязки .bind(to: textfield.rx.text).

Если вы написали ChartInfoController, я предлагаю вам изменить это работает так, как работают другие элементы пользовательского интерфейса. Если вы этого не написали, отправьте вопрос разработчику / сопровождающему.

Поддерживает ли RxSwift что-то вроде [временное отключение определенной подписки]?

Это зависит от что вы подразумеваете под «временно отключить». Он не поддерживает тихую отписку и повторную подписку, но есть много операторов, которые отфильтровывают некоторые события, которые они получают, передавая другие. Например, filter, throttle, debounce, ignoreElements ... Многие из них делают это.

Есть ли лучшая практика для решения таких ситуаций?

Тогда лучшее решение упомянуто выше.

...