問題
下記のようなActionがあるとします。
class ViewController: UIViewController {
private let action = Action<Void, Void, Never> {
SignalProducer { observer, lifetime in
let workItem = DispatchWorkItem {
observer.send(value: ())
observer.sendCompleted()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)
lifetime.observeEnded {
workItem.cancel()
}
}
}
}
下記各パターンでviewDidAppearで発火して、完了する前にViewControllerがdeinitしても、disposeされません。
Action.swift#L143のソースからもdisposeを管理していないため、disposeされないことがわかります。
private let disposable = CompositeDisposable()
override func viewDidLoad() {
super.viewDidLoad()
// パターン1
self.action <~ self.reactive.viewDidAppear
// パターン2
self.action <~ self.reactive.viewDidAppear.take(during: self.reactive.lifetime)
// パターン3
self.disposable += self.action <~ self.reactive.viewDidAppear
}
// パターン4
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.action.apply().start()
}
deinit {
self.disposable.dispose()
}
disposeする方法
Action内で生成したSignalProducerを直接disposeするしかなさそう
パターン1
<~のバインディング演算子が使えず、また入力が必要な場合もスマートに値を入力できなさそうです。
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.action.apply().take(during: self.reactive.lifetime).start()
}
パターン2
<~のバインディング演算子が使えて、ソースから値を入力できるので、パターン1よりスマートそう。
ただ、余分に拡張関数を追加する必要があるので、コード量が必要。
override func viewDidLoad() {
super.viewDidLoad()
self.reactive.action <~ self.reactive.viewDidAppear
}
private extension Reactive where Base: ViewController {
var action: BindingTarget<Void> {
self.makeBindingTarget { base, _ in
base.action.apply().take(during: base.reactive.lifetime).start()
}
}
}
パターン3
入力に合わせて、Lifetimeも送ってみる。
Lifetimeしか対応できないため、他のdisposeパターンに対応しきれない。
class ViewController: UIViewController {
private let action = Action<(Void, Lifetime), Void, Never> {
SignalProducer { observer, lifetime in
let workItem = DispatchWorkItem {
observer.send(value: ())
observer.sendCompleted()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)
lifetime.observeEnded {
workItem.cancel()
}
}
.take(during: $0.1)
}
override func viewDidLoad() {
super.viewDidLoad()
self.action <~ self.reactive.viewDidAppear.map(value: ((), self.reactive.lifetime))
}
}
パターン4
SignalProducerをActionの入力値にしてみる。
class ViewController: UIViewController {
private let action = Action<SignalProducer<Void, Never>, Void, Never> {
$0
}
override func viewDidLoad() {
super.viewDidLoad()
self.action <~ self.reactive.viewDidAppear.compactMap { [weak self] in
guard let self = self else { return nil }
return SignalProducer { observer, lifetime in
let workItem = DispatchWorkItem {
observer.send(value: ())
observer.sendCompleted()
}
DispatchQueue.main.asyncAfter(deadline: .now() + 5, execute: workItem)
lifetime.observeEnded {
workItem.cancel()
}
}
.take(during: self.reactive.lifetime)
}
}
}