Почему мы должны вызывать «disposeBy (bag)» явно после «подписки» в RxSwift - PullRequest
0 голосов
/ 16 октября 2018

Я читал об этом в блоге http://adamborek.com/memory-managment-rxswift/:

Когда вы подписываетесь на Observable, Disposable сохраняет ссылку на Observable, а Observable сохраняетсильная ссылка на Disposable (Rx создает некоторый цикл сохранения здесь).Благодаря этому, если пользователь возвращается обратно в стек навигации, Observable не будет освобожден, если вы не хотите, чтобы он был освобожден.

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

import UIKit
import RxCocoa
import RxSwift

class Button: UIButton {
    private var number: Int = 0

    private let buttonPushedSubject: PublishSubject<Int> = PublishSubject.init()
    var buttonPushedObservable: Observable<Int> { return buttonPushedSubject }

    deinit {
        print("Button was deallocated.")
    }

    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
    }

    @objc final func buttonTapped() {
        number = number + 1
        buttonPushedSubject.onNext(number)
    }
}

class ViewController: UIViewController {
    @IBOutlet private weak var button: Button!

    deinit {
        print("ViewController was deallocated.")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        button.buttonPushedObservable.subscribe(onNext: { (number) in
            print("Number is \(number)")
        }, onError: nil, onCompleted: nil, onDisposed: nil)
    }
}

И, что удивительно, после закрытия этого контроллера представления журналы выглядят так:

Number is 1
Number is 2
Number is 3
Number is 4
ViewController was deallocated.
Button was deallocated.
...

, что означает, что были освобождены как ViewController, так и Button!В этом случае я не вызывал disposeBy(bag), а компилятор выдает предупреждение.

enter image description here

Затем я начал смотреть на реализацию subscribe(onNext:...) (с / п ниже):

let disposable: Disposable

if let disposed = onDisposed {
    disposable = Disposables.create(with: disposed)
}
else {
    disposable = Disposables.create()
}

let callStack = Hooks.recordCallStackOnError ? Hooks.customCaptureSubscriptionCallstack() : []

let observer = AnonymousObserver<E> { event in

    switch event {
    case .next(let value):
        onNext?(value)
    case .error(let error):
        if let onError = onError {
            onError(error)
        }
        else {
            Hooks.defaultErrorHandler(callStack, error)
        }
        disposable.dispose()
    case .completed:
        onCompleted?()
        disposable.dispose()
    }
}
return Disposables.create(
    self.asObservable().subscribe(observer),
    disposable
)

В этомБлок кода выше, это правда, что observer содержит строгую ссылку на disposable через лямбда-функцию.Однако чего я не понимаю, так это то, как disposable содержит строгую ссылку на observer?

1 Ответ

0 голосов
/ 17 октября 2018

Пока наблюдаемая активна, существует контрольный цикл, но освобождение вашей кнопки отправляет полное событие, которое прерывает цикл.

Тем не менее, если вы сделаете что-то подобное Observable<Int>.interval(3).subscribe(), поток будетне освобождает.

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

Лучший способПодумайте об этом ... complete / error - это способ источника сообщить приемнику, что он выполнил излучение (что означает, что поток больше не нужен), и вызов dispose() для одноразового использования - это приемникиспособ сообщить источнику, что он не заинтересован в получении дополнительных событий (что также означает, что поток больше не нужен). Чтобы освободить поток, источник или приемник должны сообщить, что он завершен.

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

...