Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift optimization level 'Fastest' breaks sorting of array

I have a really strange issue. I'm sorting an array of NSDictionary objects in my app, but it only works correctly when the app is running from Xcode. As soon as I distribute the app and install & run it on a device, the sorting no longer works.

Here's the code can be run in a playground, with some example NSDictionary objects. The code in the app is the same.

import UIKit

let p1 = NSDictionary(objects: ["Zoe", 32], forKeys: ["name", "age"])
let p2 = NSDictionary(objects: ["Adrian", 54], forKeys: ["name", "age"])
let p3 = NSDictionary(objects: ["Jeff", 23], forKeys: ["name", "age"])
let p4 = NSDictionary(objects: ["", 66], forKeys: ["name", "age"])
let p5 = NSDictionary(objects: [23], forKeys: ["age"])

let persons = [p1,p2,p3,p4,p5]

let sortedPersons = persons.sorted { (p1, p2) -> Bool in
    (p2["name"] as? String) > (p1["name"] as? String)
}

As you can see, sorting in the playground does work correctly. Does anyone know what could be wrong?

Update

I found that the Swift Optimization Level is causing the problem. Setting this to -O (Fastest) will cause the sort to fail. Setting it to -Onone (None) will cause the sort to work properly.
Does anyone have any suggestions on how to change the code, so it will work with -O optimization?

Update 2

I've filed a bug report at Apple. For the time being I'm using an NSSet to sort the array, which seems to be working fine.

Last update

I haven't been able to reproduce this since Xcode 6.1.1

like image 839
René Avatar asked Nov 10 '14 10:11

René


1 Answers

This appears to be down to your naming convention within your sorted closure. Changing (p1, p2) to different names will resolve it. With -Ofastest, the compiler seems to be incorrectly doing 2 things:

1) causing p1 and p2 within the closure to refer to the NSDictionarys themselves rather than the closure parameters

2) cleaning up the references to the NSDictionary objects prematurely, given #1

Change the code so the last section shows:

let sortedPersons = persons.sorted { (d1, d2) -> Bool in
    (d2["name"] as? String) > (d1["name"] as? String)
}
like image 120
DJF Avatar answered Sep 19 '22 13:09

DJF