Reputation: 7648
I have an UITabBar inside a ViewController. Selecting tab bar items I add different child view controller. And remove the others.
But I never see the deint log on the console, only the viewWillDisappear..
How can I be sure to deint the child viewcontroller to save memory?
this is how I create the child controller:
private lazy var lotteryViewController: LotteryViewController = {
// Load Storyboard
let storyboard = UIStoryboard(name: "TrophyRoom", bundle: Bundle.main)
// Instantiate View Controller
var viewController = storyboard.instantiateViewController(withIdentifier: "LotteryViewController") as! LotteryViewController
// Add View Controller as Child View Controller
self.add(asChildViewController: viewController)
return viewController
}()
add child controller:
private func add(asChildViewController viewController: UIViewController) {
// Add Child View Controller
addChildViewController(viewController)
// Add Child View as Subview
containerView.addSubview(viewController.view)
// Configure Child View
viewController.view.frame = containerView.bounds
viewController.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Notify Child View Controller
viewController.didMove(toParentViewController: self)
}
remove method called from the father viewController:
private func remove(asChildViewController viewController: UIViewController) {
// Notify Child View Controller
viewController.willMove(toParentViewController: nil)
// Remove Child View From Superview
viewController.view.removeFromSuperview()
// Notify Child View Controller
viewController.removeFromParentViewController()
}
Log methods of the child controllers:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
print("Sessions View Controller Will Appear")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("Sessions View Controller Will Disappear")
}
deinit {
NotificationCenter.default.removeObserver(self)
// Log if a view controller is being deinited
print("\nDeinit: \(self.description)\n")
}
Upvotes: 1
Views: 1006
Reputation: 535240
Class instance memory management works by keeping a count of retaining references.
deinit
means "this object is about to go out of existence because there are no more retaining references to it".
You are doing the parent-child "dance" correctly when you add a child view controller and when you remove a child view controller. So all is well from that standpoint. When you call viewController.removeFromParentViewController
, it is certainly true that one retaining reference to it goes away, namely, the parent-child retaining reference (the childControllers
array, which has removed and released the child).
But that does not mean that all retaining references go away. And indeed, the fact that deinit
is not called signals that some other retaining reference exists. All you have to do now is find out what that reference is. But in fact it appears that you have shown us what it is:
private lazy var lotteryViewController: LotteryViewController = {
let storyboard = UIStoryboard(name: "TrophyRoom", bundle: Bundle.main)
var viewController = storyboard.instantiateViewController(withIdentifier: "LotteryViewController") as! LotteryViewController
self.add(asChildViewController: viewController)
return viewController
}()
lotteryViewController
is a strong (retaining) reference to the child view controller. Thus, it keeps that view controller alive even after you've removed it as a child.
So there's a weakness in (or at least an unexpected consequence of) your architecture (by which I mean your curious use of a lazy var
instance property with a define-and-call initializer). The way to make a child view controller is to instantiate the view controller, hand it over to the parent as its child, and let go of the view controller so that the only reference to it is the parent. (If you need a subsequent reference to the child, you can always get it by way of the parent.) You are not doing that, so you don't get the deinit
when the child is removed from the parent.
Upvotes: 3