RxSwift и секрет Variadic DisposeBag

Используйте одну из новейших функций RxSwift, чтобы значительно очистить свой код

Моя компания уже некоторое время использует RxSwift для всех новых проектов iOS, и мы оценили его мощность, гибкость и лаконичность.

Тем не менее, есть одна область, в которой RxSwift, скажем так, несколько менее краток. Где, по сути, это совершенно излишне.

Итак, сегодня давайте поговорим о Disposables и DisposeBags.

Одноразовые и DisposeBags?

Как я уверен, вы знаете, Disposables и DisposeBags - это уступка RxSwift по сравнению с управлением памятью ARC в Swift.

Когда вы подписываетесь, привязываетесь к RxSwift Observable или переходите от него, эта подписка возвращает Disposable. Этот одноразовый элемент в основном является ссылкой на эту подписку и на всю цепочку Observable этой подписки.

Пока этот одноразовый предмет не утилизируется, цепочка подписок существует (если только подписка не получает событие завершения или ошибки, но это уже другая история).

Так. Суть в том, что подписки возвращают одноразовые предметы, которые нам необходимо поддерживать, чтобы правильно контролировать жизненный цикл подписки.

Эти одноразовые предметы для удобства обычно вставляются в DisposeBag, который был создан и прикреплен к UIViewController (или, в некоторых случаях, к модели представления или другому объекту).

А DisposeBag - это именно то, что написано: сумка (или набор) одноразовых предметов.

Так как это нам помогает?

Что ж, когда контроллер представления освобождается, его переменные (включая мешок) освобождаются. Когда disposeBag освобождается, его вызовы функции deinit размещают на всех содержащихся в нем одноразовых материалах.

Эти одноразовые объекты, в свою очередь, освобождают любые ссылки, которые у них могут быть на любые наблюдаемые объекты, которые также могут, в свою очередь, освобождать свои ссылки на свои наблюдаемые объекты и т. Д. и так далее по цепочке, пока мы не закончим.

Все исправно выпущено, ничего не просочилось, и все довольны.

За исключением одной незначительной проблемы.

Резервирование

Итак, допустим, вы используете RxSwift и используете архитектуру MVVM (Model-View-ViewModel).

На этом конкретном экране наша модель представления предоставляет набор наблюдаемых, а наш контроллер представления привязывает эти наблюдаемые к набору меток. Довольно простые вещи.

Код выглядит так.

class MVVMViewController: UIViewController {
    @IBOutlet weak var firstNameLabel: UILabel!
    @IBOutlet weak var lastNameLabel: UILabel!
    @IBOutlet weak var addressLabel: UILabel!
    @IBOutlet weak var cityLabel: UILabel!
    @IBOutlet weak var stateLabel: UILabel!
    @IBOutlet weak var zipLabel: UILabel!
    private var viewModel = MyViewModel()
    private var disposeBag = DisposeBag()
    func setupSubscriptions() {
        viewModel.firstName
            .bind(to: firstNameLabel.rx.text)
            .disposed(by: disposeBag)
    viewModel.lastName
            .bind(to: lastNameLabel.rx.text)
            .disposed(by: disposeBag)
    viewModel.address
            .bind(to: addressLabel.rx.text)
            .disposed(by: disposeBag)
    viewModel.city
            .bind(to: cityLabel.rx.text)
            .disposed(by: disposeBag)
    viewModel.state
            .bind(to: stateLabel.rx.text)
            .disposed(by: disposeBag)
    viewModel.zip
            .bind(to: zipLabel.rx.text)
            .disposed(by: disposeBag)
    }
}

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

Верно. Каждая подписка (привязка) возвращает одноразовый объект, который нужно добавить в disposeBag. Каждый его возвращает. И каждого нужно поддерживать. От этого никуда не деться.

Или есть?

Решение? Пакет Variadic DisposeBag

Теперь посмотрите следующий код.

class RxSwiftViewController: UIViewController {
    @IBOutlet weak var firstNameLabel: UILabel!
    @IBOutlet weak var lastNameLabel: UILabel!
    @IBOutlet weak var addressLabel: UILabel!
    @IBOutlet weak var cityLabel: UILabel!
    @IBOutlet weak var stateLabel: UILabel!
    @IBOutlet weak var zipLabel: UILabel!
    private var viewModel = MyViewModel()
    private var disposeBag = DisposeBag()
    func setupSubscriptions() {
        disposeBag.insert(
            viewModel.firstName.bind(to: firstNameLabel.rx.text),
            viewModel.lastName.bind(to: lastNameLabel.rx.text),
            viewModel.address.bind(to: addressLabel.rx.text),
            viewModel.city.bind(to: cityLabel.rx.text),
            viewModel.state.bind(to: stateLabel.rx.text),
            viewModel.zip.bind(to: zipLabel.rx.text)
        )
    }
}

Намного чище! Но что случилось со всеми одноразовыми предметами?

Что ж, RxSwift 4.3 добавил вариативную версию функции вставки DisposeBag. А вариативные функции могут принимать один или несколько параметров. В этом случае один или несколько предметов одноразового использования.

Потому что, если вы заглянете внутрь disposed (by: disposeBag), вы обнаружите, что это всего лишь одна строка кода: disposeBag.insert (self).

Поэтому вместо того, чтобы брать результат каждой подписки и функционально связывать его с dispose (by :), мы просто обходим посредника и сами добавляем каждый из них непосредственно в disposeBag.

Думаю, вы согласитесь с тем, что код определенно более лаконичен и что, удалив часть шаблонов, мы сможем лучше выразить цель функции.

Наслаждаться.

Кстати, если вы сочтете полезной вставку с переменными числами в DisposeBag, милости просим. Это был один из моих незначительных вкладов в проект RxSwift. ;)