简体   繁体   中英

Swift iOS -Should Deinit get called inside child View Controller when added as a child to another View Controller?

I have a childVC(vc3) inside a parentVC(vc2) inside another parentVC(vc1). I'm doing it this way for animation purposes.

What happens is I add vc3 as a child to vc2. I have a collectionView that pushes on vc1. Once vc1 is on scene vc2 is added to it. When I pop vc1 off the stack and go back to the collectionView the deinit in vc1 gets called however the deinit in vc2 never gets called.

Is the deinit in vc2 supposed to get a called even though it's a child of vc1? Or is it possibly because the thirdVC is creating a strong reference to itself inside of the secondVC?

SecondVC with the ThirdVC added inside of it:

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

CollectionView:

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

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

FirstVC:

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

The answer to my question is Yes the child View Controller's deinit should also run. If you call deinit inside the child view controller and when the parent is popped of the scene (or dismissed) and the child's deinit doesn't run then you have a problem.

As @BadhanGanesh pointed out in the comments he asked:

" are you using any notification observers that you fail to remove them when not needed "

And @Bruno Pinheiro suggested in the comments:

" It seems to be a strong reference issue "

They were both right. I looked through all the code and I thought I had removed a KVO observer but I didn't.

Long story short if you have a View Controller that is a child of another View Controller (parent) then once the parent is deallocated then so should the child.

If your using any KVO observers inside either the parent or child then make sure you remove them or you will create a strong retain cycle.

I needed to pass "weak self" to firebase observer in parent view controller to remove the retain cycle, then deinit was called on both parent and child controllers:

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

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