iOS: How to check if UIViewControllers are unloading? (Swift)

Richard picture Richard · Nov 16, 2014 · Viewed 11.5k times · Source

I'm using a UISplitViewController every time I click on a row in the Master VC I can see that viewDidLoad() is run in the Detail VC.

Does this mean i'm creating a new instance of Detail VC each row click?

If so, how can I check that the Detail VC are unloading correctly and that i'm not just creating more and more new Detail VCs?

I'm a bit lost here in Swift. Previously I could NSLog in dealloc() and see the UIViewController correctly unloading.

I here Swift has a deinit function but this is never called:

deinit {
    println("\(__FILE__.lastPathComponent)) : \(__FUNCTION__)")
    NSNotificationCenter.defaultCenter().removeObserver(self)
}

1) Where should I be removing my observers?

2) When I look in the Debug Navigator in Xcode the Memory usage just keeps going up and never down.

Updated: Detail VC is being called as follows:

if segue.identifier == "addEvent" {
    if let controller = (segue.destinationViewController as UINavigationController).topViewController as? ManageViewController {
        controller.manageEvent = nil
        controller.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
        controller.navigationItem.leftItemsSupplementBackButton = true
    }
}

I'm not doing anything different than lots of examples i've seen, but I am worried about deinit not being called

Updated: Working now - Problem was with delegate stopping deinit being called (see below answer)

My original Non-Working code was:

protocol ManageViewDelegate {
    func pressedButton(sender: AnyObject)
}

class ManageView: UIView {
    var delegate: ManageViewDelegate? = nil
    ...
}

New Working code:

protocol ManageViewDelegate: class {
    func pressedButton(sender: AnyObject)
}

class ManageView: UIView {
    weak var delegate: ManageViewDelegate? = nil
    ...
}

Answer

Rob picture Rob · Nov 17, 2014

You have a view with a delegate property that references back to the view controller. This will result in a strong reference cycle (previously known as a retain cycle) because the view controller is maintaining a strong reference to its top level view which is, in turn, maintaining a strong reference back to the view controller.

In the Resolving Strong Reference Cycles Between Class Instances section of The Swift Programming Language: Automatic Reference Counting, Apple describes how to address this:

Swift provides two ways to resolve strong reference cycles when you work with properties of class type: weak references and unowned references.

Weak and unowned references enable one instance in a reference cycle to refer to the other instance without keeping a strong hold on it. The instances can then refer to each other without creating a strong reference cycle.

Use a weak reference whenever it is valid for that reference to become nil at some point during its lifetime. Conversely, use an unowned reference when you know that the reference will never be nil once it has been set during initialization.

Thus, you can resolve your strong reference cycle by defining the delegate to be weak:

weak var delegate: ManageViewDelegate? 

For that to work, you have to specify your protocol to be a class protocol:

protocol ManageViewDelegate: class {
    // your protocol here
}

That will resolve the strong reference cycle and eliminates the need to manually nil the delegate in order to resolve the strong reference cycle.