Swift 2.1 Error sorting in place, only on release

2019-01-26 07:38发布

问题:

I started getting crash reports for the sort lamdba in the below code, the third line in the grey box below:

private func fixOverlaps(inout blocks: [TimeBlock], maxOverlaps: Int? = nil) {
    blocks.sortInPlace { a,b in
        if a.startTime < b.startTime {
            return true
        } else if a.startTime == b.startTime && a.endTime < b.endTime {
            return true
        }
        return false
    }
...

Note the crash does not occur on debug builds from XCode. Only the App Store and Ad Hoc archives will crash, and only when the length of the blocks list is in the hundreds.

I modified the code to this, and the problem went away:

private func fixOverlaps(inout blocks: [TimeBlock], maxOverlaps: Int? = nil) {
    blocks = blocks.sort { a,b in
        if a.startTime < b.startTime {
            return true
        } else if a.startTime == b.startTime && a.endTime < b.endTime {
            return true
        }
        return false
    }
...

Is there something I've missed about how to use inout or sortInPlace? I can try to do a demo of this. It's on multiple versions of iOS (8/9) and Swift 2.1.

EDIT--------------------

Ok here's a minimal version that crashes. Turns out the inout was a red herring. If you start a new single view project in XCode 7.1, you can replace the view controller with this:

class ViewController: UIViewController {

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.

    var blocks = [TimeBlock]()
    for var i in 0...20 { //Works if you put in a small number like 8
        let t = TimeBlock()
        t.start = Int(arc4random_uniform(1000)) //Get some random numbers so the sort has to do some work
        t.end = Int(arc4random_uniform(1000))
        blocks.append(t)
    }

    blocks.sortInPlace { a,b in
        if a.start > b.start {
            return true
        }
        return false
    }

    print("done") //Gets here on debug, not release
}

class TimeBlock {
    var start = 0
    var end = 0
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


}

So run it in release, and you should see it prints "Done" if you end the loop at around 17 but crashes with 20. Exact number might be different for you.

回答1:

This code looks correct. It sounds like you've run across a bug in the compiler, which is generally the case when you have a crash in release configuration but not debug. You can perhaps verify this by enabling optimizations in your debug build and testing to see if it generates the problem. Aside from your workaround, the only other thing you need to do is file a bug.



回答2:

Worked around this without loss of functionality by using self.list = self.list.sort() instead of self.list.sortInPlace().



回答3:

This is a bug in Xcode 7.1 . Turning the swift compiler optimization level from fast to none fixed this problem for me.



回答4:

I logged a bug on bugs.swift.org about this earlier today and received a prompt response from one of the developers that this is indeed an issue with Xcode 7.1. He pointed out that its resolution is outlined in the Xcode 7.2 Release Notes:

A bug in the optimizer has been fixed that caused in-place sort on mutable collections to crash. (23081349)

So using Xcode 7.2 to compile should also fix the issue.