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?
After lots of experimenting I think I found out that
NSArrayControllerproduces a memory leak as soon as you callfetch(_:)orpreopareContent(). It seems that it retains itsmanagedObjectContextand 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
NSTableViewDataSourceimplementation.