macoscocoacore-dataappkitnsarraycontroller

Crash when setting NSArrayController's managedObjectContext to nil


I have a NSDocument based macOS app with Core Data which by its nature can only ever have one document open at a time. Therefore, when a new document is opened I close the currently open one. All document related UI is in a separate window controller and everything works well.

But I also have a menu bar item that toggles a separate window which displays some information about the document. The UI is a simple NSTableView bound to an NSArrayController. The array controller's managagedObjectContext property is set when the current document changes. This always leads to a crash with EXC_BAD_INSTRUCTION.

To narrow down the issue I completely removed all bindings and any other manipulation of the array controller. The crash is gone. I also created a new testArrayController in code to see what happens there and sure enough I could reproduce the crash:

let testArrayController = NSArrayController()

var document: Document? {
    didSet {
        if document != nil {
            testArrayController.managedObjectContext = document?.managedObjectContext
            testArrayController.prepareContent()  // <---- this causes the crash later on
        } else {
            testArrayController.managedObjectContext = nil
        }
    }
}

override func viewDidLoad() {
    super.viewDidLoad()

    testArrayController.entityName = "MyEntity"
    ...
}

It seems that calling prepareContent() somehow locks the array controller to a specific managedObjectContext and causes the crash when it is set to nil.

How can I safely "deactivate" an NSArrayController, or change its managedObjectContext?


Solution

  • After lots of experimenting I think I found out that NSArrayController produces a memory leak as soon as you call fetch(_:) or preopareContent(). It seems that it retains its managedObjectContext and never releases it. Even though all other references to the controller have been released I could see the leaked instance in the memory debugger.

    I worked around the issue by replacing bindings with a regular NSTableViewDataSource implementation.