問題
下記のような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)
}
}
}