Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mysterious crashes in Swift 1.2 - in Release builds only

After updating to Xcode 6.3 (beta 1) and Swift 1.2, all my apps are mysteriously crashing in Release builds only. They work fine, after updating my code for Swift 1.2, in Debug builds. The debugger gives no sense of where the crashes are occurring, and it isn't clear why. Some of the crashes are

malloc: *** error for object 0x7ff0c3824800: pointer being freed was not allocated *** set a breakpoint in malloc_error_break to debug

Others are "unrecognized selector", but they make no sense; the objects to which the selectors are being sent are not even objects I'm aware of using. It appears that something has gone wrong with memory management, so that one object is being substituted for another.

What on earth could be causing this? With nothing useful in the call stack (so that I don't even know where in my code the crash occurs) and no variables showing up in the variables pane of the debugger when I step through my code (so that I can't even look at the values of things), how could I even begin to track it down?

like image 889
matt Avatar asked Feb 14 '15 03:02

matt


1 Answers

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.

like image 55
matt Avatar answered Oct 24 '22 03:10

matt