【问题标题】:Swift iOS -Should Deinit get called inside child View Controller when added as a child to another View Controller?Swift iOS - 当作为子视图添加到另一个视图控制器时,是否应该在子视图控制器中调用 Deinit?
【发布时间】:2018-08-23 01:16:32
【问题描述】:

我在另一个 parentVC(vc1) 中的 parentVC(vc2) 中有一个 childVC(vc3)。我这样做是为了动画目的。

发生的情况是我将 vc3 作为子级添加到 vc2。我有一个推动 vc1 的 collectionView。一旦 vc1 出现在场景中,就会将 vc2 添加到其中。当我将 vc1 从堆栈中弹出并返回到 collectionView 时,vc1 中的 deinit 会被调用,但是 vc2 中的 deinit 永远不会被调用。

vc2 中的 deinit 是否应该被调用,即使它是 vc1 的子代?还是可能是因为 thirdVC 在 secondVC 内部创建了对自身的强引用?

在其中添加了 ThirdVC 的 SecondVC:

class SecondController: UIViewController {

override func viewDidLoad() {
        super.viewDidLoad()

        let thirdVC = ThirdController()
        addChildViewController(thirdVC)
        view.addSubview(thirdVC.view)
        thirdVC.didMove(toParentViewController: self)
}

 // this never runs when the firstVC is popped off the stack
deinit{
     print("the secondVC has be deallocated")
}
}

集合视图:

func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {

        let firstVC = FirstController()
        navigationController?.pushViewController(firstVC, animated: true)
    }

第一VC:

class FirstController: UIViewController {

override func viewDidLoad() {
        super.viewDidLoad()

        let secondVC = SecondController()
        addChildViewController(secondVC)
        view.addSubview(secondVC.view)
        secondVC.didMove(toParentViewController: self)
}

// this runs when popped off the stack
deinit{
     print("the firstVC has be deallocated")
}
}

【问题讨论】:

  • 这似乎是一个强引用问题,但是,UIViewController.parent 引用是弱的。所以,我认为 vc3 并没有强烈引用 vc2。代表很弱,所以也不应该是这样。您是否在代码的任何其他位置检索这些视图控制器?
  • @BrunoPinheiro 嘿,感谢您的帮助。 secondVC 将在其他地方访问,因为它内部具有动画功能。例如,我有一个 tabBarController,在第一个选项卡上有一个 collectionView,它推动 vc1,这意味着 vc2 也将出现在场景中(因为它是 vc1 的子级)。如果我在 tabBar 上按下另一个选项卡,那也将有一个 collectionView 可以推送另一个 vc(如 zeroVC),该 vc 也将第二个 VC 作为子级。基本上,两个选项卡现在都将 secondVC 显示为其父级 -firstVC 和 zeroVC 的子级。
  • @LanceSamaria 您在多个地方使用secondVC 的相同实例?您确定没有其他对secondVC 的强引用吗?
  • @BadhanGanesh 是的,没有,在这一点上这是一个小项目,所以 secondVC 仅在这两个地方实例化 -firstVc 和 zeroVC。问题是即使没有推送 zeroVC 并且唯一拥有 secondVC 副本的是 firstVC,在弹出 firstVC 时也不会调用 secondVC deinit。我什至在导航器中使用 Find 进行了全局搜索,唯一实例化它的地方是 firstVC(我从 zeroVC 中删除了它)。
  • @LanceSamaria 好的。您是否正在使用强烈引用您的 secondVC 实例的闭包?或者您是否在使用任何通知观察者,但在不需要时没有删除它们?

标签: ios swift uiviewcontroller retain-cycle deinit


【解决方案1】:

我的问题的答案是是的,子视图控制器的deinit 也应该运行。如果您在子视图控制器中调用 deinit 并且当父级弹出场景(或被解雇)并且孩子的 deinit 没有运行,那么你就有问题了。

正如@BadhanGanesh 在他问的 cmets 中指出的那样:

你是否使用了任何通知观察者,但在不需要时没有删除它们

@Bruno Pinheiro 在 cmets 中建议:

"好像是强引用问题"

他们都是对的。我查看了所有代码,我以为我已经删除了一个 KVO 观察者,但我没有。

长话短说,如果您的 View Controller 是另一个 View Controller(父级)的子级,那么一旦父级被释放,子级也应该如此。

如果您在父级或子级内部使用任何 KVO 观察者,请确保删除它们,否则您将创建一个强大的保留周期。

【讨论】:

  • 不错!感谢您分享解决方案 :) 很高兴我以某种方式帮助了您!
【解决方案2】:

我需要在父视图控制器中将“弱自我”传递给 firebase 观察者以删除保留周期,然后在父控制器和子控制器上调用 deinit:

    func observeFeatureChanged(){
    microbeRef.queryOrdered(byChild: Nodes.TIMESTAMP)
        .observe(.childChanged) { [weak self] (dataSnapshot) in
            if let featureDic = dataSnapshot.value as? [String: Any]{
                let featureName = dataSnapshot.key
                if let index = self?.featureNames.firstIndex(of: featureName){
                    self?.featureNames[index] = featureName
                    self?.featureDictionaries[index] = featureDic
                    let indexpath = IndexPath(item: index, section: 0)
                    self?.tableView.reloadRows(at: [indexpath], with: .automatic)
                }
            }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-11
    • 2012-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-03
    • 2017-01-18
    相关资源
    最近更新 更多