Swift's NSNumber bridging is properly broken
| Originator: | automate.mantasystems | ||
| Number: | rdar://22011202 | Date Originated: | 2015-07-27 |
| Status: | Open | Resolved: | |
| Product: | Developer Tools | Product Version: | Xcode Version 7.0 beta 4 (7A165t) |
| Classification: | Serious Bug | Reproducible: | Always |
Summary: Swift treats all NSNumber instances as Bool, Int, and Double, regardless of actual numerical type. e.g. *All* of the following always test as *true:* import Cocoa let b:AnyObject = NSNumber(bool:true) let n:AnyObject = NSNumber(int:100) let f:AnyObject = NSNumber(double:1.1) b is Bool // true b is Int // true b is Double // true n is Bool // true n is Int // true n is Double // true f is Bool // true f is Int // true f is Double // true Aside from being wrong and unhelpful, this is also inconsistent with behaviors of native Swift Bools, Ints, and Doubles: import Cocoa let b:Any = true let n:Any = 100 let f:Any = 1.1 b is Bool // true b is Int // false b is Double // false n is Bool // true n is Int // false n is Double // false f is Bool // true f is Int // false f is Double // false This creates further confusion when native Swift Bools, Ints, and Doubles are stored in variables of type AnyObject, at which point Swift's not-so-seamless-after-all Cocoa bridging kicks in, transmogrifying them into Cocoa objects and destroying their own Type information in the process: import Cocoa let b:AnyObject = true let n:AnyObject = 100 let f:AnyObject = 1.1 b is Bool // true b is Int // true b is Double // true n is Bool // true n is Int // true n is Double // true f is Bool // true f is Int // true f is Double // true b.dynamicType // __NSCFBoolean.Type n.dynamicType // __NSCFNumber.Type f.dynamicType // __NSCFNumber.Type (This gets especially irritating in Swift code that creates the correct Swift Bool/Int/Double representation of a particular value, only to have that representation summarily trashed the moment that value passes through a Cocoa-friendly API.) Just to round things out, storing NSNumber instances in variables of type Any does not produce the converse effect (i.e. NSNumbers are not transmogrified to Bools/Ints/Doubles): import Cocoa let b:Any = NSNumber(bool:true) let n:Any = NSNumber(int:100) let f:Any = NSNumber(double:1.1) b is Bool // true b is Int // true b is Double // true n is Bool // true n is Int // true n is Double // true f is Bool // true f is Int // true f is Double // true b.dynamicType // __NSCFBoolean.Type n.dynamicType // __NSCFNumber.Type f.dynamicType // __NSCFNumber.Type Obviously, the NSNumber class cluster is a dead loss as far as compile-time type checking is concerned, but between their private __NSCFBoolean and __NSCFNumber representations and NSNumber's objCType property there is sufficient runtime information available for Swift to handle them correctly during execution. (The alternative would be not to bridge NSNumber instances automatically, UNLESS the variable's type is totally unambiguous, i.e. Bool OR Int OR Double, NOT Any/AnyObject. Though this would be the less desirable choice, and would no doubt cause irritations of its own.) Version: Xcode Version 7.0 beta 4 (7A165t) OS X 10.10.4 (14E46) Notes: No doubt this is a dupe, but hopefully this issue gets enough pile-on for it to be fixed before Swift 2.0 final ships, because it's damnably annoying and clearly lends the lie to Swift's ostensibly "seamless" Cocoa bridging.
Comments
Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!