0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ReactiveSwiftのActionで発火したSingalProducerをDisposeする方法の検討

Posted at

問題

下記のような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で発火して、完了する前にViewControllerdeinitしても、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

SignalProducerActionの入力値にしてみる。

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)
        }
    }
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?