简体   繁体   中英

RxSwift; ViewController never deinitialized

I have a ViewController (called MainViewController ) which is backed by a ViewModel (called MainViewModel ).

The ViewModel has a variable that defines which child ViewController MainViewController should present as its child.

My problem is that, when a child is removed in favor of another child, it never gets deinit 'ed.

Here's the code:

MainViewController:

class MainViewController: UIViewController {

    var viewModel: MainViewModel!
    private let disposeBag = DisposeBag()

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.viewController
            .subscribe(onNext: { [weak self] vc in
                self?.newVc(vc)
            })
            .disposed(by: disposeBag)
    }

    static func instantiate(viewModel: MainViewModel) -> MainViewController {
        let vc = MainViewController()
        vc.viewModel = viewModel
        return vc
    }

    private func newVc(_ vc: UIViewController) {
        remove(childViewController: children.first)
        addFullScreen(childViewController: vc)
    }
}

MainViewModel:

class MainViewModel {

    lazy var viewController: Observable<UIViewController> = {
        return Observable.just(ColorViewController(.green))
            .delay(RxTimeInterval.seconds(3), scheduler: MainScheduler.instance)
            .startWith(ColorViewController(.yellow))
    }()

}

You see in MainViewModel s viewController variable, that it first emits a yellow ColorViewController, and 3 seconds later a green one. ColorViewController is a basic subclass of UIViewController, with a colored view, and a the deinit -method overwritten. This methods isn't called, when the yellow ColorViewController is removed..

Who holds the reference to that yellow ColorViewController, and how to fix it?

Bonus code:

extension UIViewController {

    public func addFullScreen(childViewController child: UIViewController) {
        guard child.parent == nil else { return }

        addChild(child)
        view.addSubview(child.view)

        child.view.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            view.leadingAnchor.constraint(equalTo: child.view.leadingAnchor),
            view.trailingAnchor.constraint(equalTo: child.view.trailingAnchor),
            view.topAnchor.constraint(equalTo: child.view.topAnchor),
            view.bottomAnchor.constraint(equalTo: child.view.bottomAnchor)
        ])

        child.didMove(toParent: self)
    }

    public func remove(childViewController child: UIViewController?) {
        guard let child = child else { return }
        guard child.parent != nil else { return }

        child.willMove(toParent: nil)
        child.view.removeFromSuperview()
        child.removeFromParent()
    }
}

UPDATE:

So I changed the viewController -variable to this:

lazy var viewController: Observable<UIViewController> = {
        return Observable<Int>.interval(RxTimeInterval.seconds(3), scheduler: MainScheduler.instance)
            .scan(0, accumulator: { (prev, next) -> Int in return prev + 1 })
            .map { index -> UIViewController in
                let modul = index % 3
                print("Index: \(index): \(modul)")
                switch modul {
                case 0: return ColorViewController(.yellow, tag: "Yellow")
                case 1: return ColorViewController(.blue, tag: "Blue")
                case 2: return ColorViewController(.green, tag: "Green")
                default: return ColorViewController(.red, tag: "Red")
                }
            }.startWith(ColorViewController(.cyan, tag: "Initial 1"),
                        ColorViewController(.purple, tag: "Initial 2"))
            .take(10)
    }()

Now I see that all the ColorViewController 's generated in the .map are deinitialized as expected. But the two passed into .startWith , are never deinitialized, not even after .take(10) causes the Observable to complete. Does that make sense to anyone?

Open up the visual memory debugger to find out who is holding on to the view controller that you want to have released. Here is an article about it: https://useyourloaf.com/blog/xcode-visual-memory-debugger/

And a video from Apple: https://developer.apple.com/videos/play/wwdc2018/416

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM