【问题标题】:RxSwift; ViewController never deinitialized迅捷; ViewController 从未取消初始化
【发布时间】:2019-12-31 14:59:38
【问题描述】:

我有一个由 ViewModel(称为 MainViewModel)支持的 ViewController(称为 MainViewController)。

ViewModel 有一个变量,用于定义应将哪个子 ViewController MainViewController 显示为其子。

我的问题是,当一个孩子被移除以支持另一个孩子时,它永远不会得到deinit'ed。

代码如下:

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))
    }()

}

您在MainViewModels viewController 变量中看到,它首先发出一个黄色的 ColorViewController,3 秒后发出一个绿色的。 ColorViewController 是 UIViewController 的基本子类,具有彩色视图,并覆盖了 deinit 方法。当黄色的 ColorViewController 被移除时,这个方法不会被调用..

谁拥有对黄色 ColorViewController 的引用,以及如何修复它?

奖励代码:

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()
    }
}

更新:

所以我将viewController-变量更改为:

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)
    }()

现在我看到.map 中生成的所有ColorViewController 都已按预期取消初始化。但是这两个传递给.startWith,永远不会被取消初始化,即使在.take(10) 导致Observable 完成之后也是如此。这对任何人都有意义吗?

【问题讨论】:

  • 坦率地说,你不应该首先将 UIViewControllers 包装在 Observable 中。它们适用于值类型(枚举、结构和不可变类),而不适用于活动对象......

标签: ios swift rx-swift deinit


【解决方案1】:

打开可视化内存调试器,找出谁在持有您想要释放的视图控制器。这是一篇关于它的文章:https://useyourloaf.com/blog/xcode-visual-memory-debugger/

还有一段来自 Apple 的视频:https://developer.apple.com/videos/play/wwdc2018/416

【讨论】:

    猜你喜欢
    • 2020-07-06
    • 2020-07-09
    • 2021-03-16
    • 1970-01-01
    • 2020-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多