Amazingly, I did track this down, mostly by deleting code in large swatches until I was down to just this (it's a view controller):
class LessonListController: UIViewController {
var terms : [Term]
// var terms : NSArray
init(terms data:NSArray) {
let arr = data.sortedArrayUsingDescriptors([NSSortDescriptor(key: "lessonSection", ascending: true)])
self.terms = arr as! [Term]
// self.terms = arr
super.init(nibName:"LessonList", bundle:nil)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@IBAction func doDismiss(sender: AnyObject) {
self.dismissViewControllerAnimated(true, completion: nil)
}
}
If (in a Release build) we present this view controller and then dismiss it, we crash on dismiss - in dealloc, which proves my theory that it's a problem with memory management.
Having isolated the code, I was able to try various alternatives. It is clear that the problem is the property var terms : [Term]
(because the only thing Swift is doing under the hood in dealloc
is releasing this array). The value of this property, as you can see in my init
, is an NSArray that has come from Cocoa (thru sortedArrayUsingDescriptors
) and has been cast to a Swift array. By trial and error, I discovered:
If we change the implementation so that the property is an NSArray (see the commented-out alternative lines), we don't crash.
Or, if we don't sort (so that this NSArray doesn't come from Cocoa), we don't crash.
Or (wait for it), if we replace self.terms = arr as! [Term]
with self.terms = arr as NSArray as! [Term]
, we don't crash!
But that third alternative is a workaround. I went through all my code in all my apps looking for as! [SomeType]
casts and replaced them all with as NSArray as [SomeType]
, and all my crashes went away!!
My theory is that something is going wrong with Swift's memory management in the optimized Release build just in the very specific situation where an NSArray arrives from Cocoa and is bridged for us to an [AnyObject]
before our code can get hold of it. Such an NSArray is not crossing the bridge properly. But by casting to NSArray and then back to down to the specific [SomeType]
Swift array, the problem is solved.
Naturally, I assume that when Apple figures this out, they'll fix it and then we can stop using this workaround. But until then, my apps are running in a Release build once again.