Swift iOS - Должен ли Deinit вызываться внутри дочернего View Controller при добавлении в качестве дочернего элемента к другому View Controller?

У меня есть childVC(vc3) внутри parentVC(vc2) внутри другого parentVC(vc1). Я делаю это так для анимации.

Что происходит, я добавляю vc3 как ребенка к vc2. У меня есть collectionView, который выдвигает на vc1. Как только vc1 находится на сцене, к нему добавляется vc2. Когда я вытаскиваю vc1 из стека и возвращаюсь к collectionView, вызывается deinit в vc1, однако никогда не вызывается deinit в vc2.

Должен ли deinit в vc2 вызываться, даже если он является потомком vc1? Или это возможно потому, что третий ВК создает сильную ссылку на себя внутри второго ВК?

SecondVC с добавленным ThirdVC:

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

1 ответ

Решение

Ответ на мой вопрос: " Да, ребенок". deinit также должен бежать. Если вы вызываете deinit внутри дочернего контроллера представления и когда родитель выталкивается из сцены (или увольняется), а дочерний deinit не запускается, тогда у вас есть проблема.

Как отметил @BadhanGanesh в комментариях, он спросил:

" Используете ли вы какие-либо уведомления наблюдателей, что вы не можете удалить их, когда они не нужны "

И @Bruno Pinheiro предложил в комментариях:

" Кажется, это серьезный вопрос "

Они оба были правы. Я просмотрел весь код и подумал, что удалил наблюдателя из КВО, но не сделал этого.

Короче говоря, если у вас есть View Controller, который является дочерним по отношению к другому View Controller (родительскому), то после того, как родительский объект освобождается, должен поступить и дочерний.

Если вы используете каких-либо наблюдателей KVO внутри родителя или потомка, убедитесь, что вы удалили их, иначе вы создадите сильный цикл сохранения.

Мне нужно было передать "weak self" наблюдателю 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)
                }
            }
    }
}
Другие вопросы по тегам