How to avoid retain cycle when assigning NSDocument as NSViewController's representedObject

38 views Asked by At

I am building a macOS Document based application. I am following Apple's example code, which can be found here.

In Apple's example, the NSDocument assigns itself as the NSViewController's representedObject. The NSDocument also retains a strong reference to the NSViewController. As far as I can see, this should cause a retain cycle, which indeed happens in my implementation of the code. But in Apple's example, it does not cause a retain cycle, and I can't work out why. Here is Apple's example code:

class Document: NSDocument {
    
    var contentViewController: ViewController!
    
    override func makeWindowControllers() {
        // Returns the storyboard that contains your document window.
        let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
        if let windowController =
            storyboard.instantiateController(
                withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as? NSWindowController {
            addWindowController(windowController)
            
            // Set the view controller's represented object as your document.
            if let contentVC = windowController.contentViewController as? ViewController {
                contentVC.representedObject = content
                contentViewController = contentVC
            }
        }
    }
}

The only difference in my implementation is that my ViewController is further down the hierarchy, as it is contained within an NSSplitViewController, so my NSDocument subclass finds it like so:

override func makeWindowControllers() {
    // Returns the Storyboard that contains your Document window.
    let storyboard = NSStoryboard(name: NSStoryboard.Name("Main"), bundle: nil)
    let windowController = storyboard.instantiateController(withIdentifier: NSStoryboard.SceneIdentifier("Document Window Controller")) as! MainWindowWC
    addWindowController(windowController)
    
    if let splitVC = windowController.contentViewController as? NSSplitViewController {
        
        for item in splitVC.splitViewItems {
            if let vc = item.viewController as? ViewController {
                vc.representedObject = self
                contentViewController = vc
            }
        }
    }
}

I can't see that this is causing the problem, and indeed when I close the window, the NSWindowController and NSSplitViewController both successfully deinitialise, but the NSViewController and NSDocument are retained.

I can solve the retain cycle by creating a weak reference to the NSDocument in the NSViewController, instead of using the .representedObject property. but it would be great to know why Apple's code is not causing the retain cycle I think it should.

Thanks,

Dan

0

There are 0 answers