问题
In the former version, to get a float value from a [String: Any]
dictionary, I can use let float = dict["somekey"] as? Float
, but in swift4.1, it doesn't work. It seems the type of dict["somekey"]
has been implicitly inferred as Double
before I get it, so casting from Double
to Float
always fails. I wonder if it is a new characteristic or just a bug.
--Here is the update.
I re-downloadeded an Xcode9.2 and did some experiments, now I think I figure out what's going on. Here is the test code:
let dict: [String : Any] = ["key": 0.1]
if let float: Float = dict["key"] as? Float {
print(float)
} else {
print("nil")
}
let dict1: [String : Any] = ["key": NSNumber(value: 0.2)]
if let float: Float = dict1["key"] as? Float {
print(float)
} else {
print("nil")
}
let number = NSNumber(value: 0.3)
if let float: Float = number as? Float {
print(float)
} else {
print("nil")
}
let number1 = NSNumber(floatLiteral: 0.4)
if let float = number1 as? Float {
print(float)
} else {
print("nil")
}
Running this code in Playground of Swift4 and Swift4.1, the results are different. In Swift4, the results are nil
0.2
0.3
0.4
, and In Swift4.1 the results are nil
nil
nil
nil
. From the result, I can learn two points:
1. When we convert JSON data into a [String : Any] dictionary
with the JSONSerialization
class, the numeric value is saved as an NSNumber
object, but not Int
, Double
or Float
.
2. In Swift4, we can use let float = NSNumberOjbect as? Float
to get a Float
value, but in Swift4.1 we can't. But still, we can get Int
or Double
value in this way, either in Swift4 or Swift4.1.
Finally again, is this a new feature or a bug? If someone knows, can you guys show up the announcement link?
回答1:
You need to distinguish two cases (in Swift 3, it was three cases):
Any
containing a Swift nativeDouble
Any
containing anNSNumber
(In Swift 3, there was type preserving NSNumber case other than the normal NSNumber
.)
When you create a native Swift Dictionary
such as [String: Any]
, and set Double
value in a normal way like this in your update:
let dict: [String : Any] = ["key": 0.1]
In this case, Any
holds the metadata representing Double
and the raw value 0.1
as Double
.
And casting Any
holding Double
to Float
always fails. As Double
to Float
cannot be converted with as
-castings.
let dblValue: Double = 0.1
if let fltValue = dblValue as? Float { //<-Cast from 'Double' to unrelated type 'Float' always fails
print(fltValue)
} else {
print("Cannot convert to Float")
}
//->Cannot convert to Float
But, in case of Any
holding NSNumber
, as always happens when the Array
or Dictionary
is bridged from NSArray
or NSDictionary
, the behaviors are different between former Swifts and Swift 4.1.
The result of JSONSerialization
matches this case.
In former Swifts, (normal) NSNumber
to Float
was an always-success operation.
And in Swift 4.1, the behavior has changed with the new feature which I have shown in my comment:
SE-0170 NSNumber bridging and Numeric types
I omit the third case once found in Swift 3, it's past.
But it is very important how you get your Dictionary
or Array
, and how you set the value to them, to solve the issue Any
to Float
.
Finally again, is this a new feature or a bug? If someone knows, can you guys show up the announcement link?
This is a new feature, not a bug. See the link above, and related threads below.
Unable to bridge NSNumber to Float Swift 3.3
Unexpected behavior when casting an NSNumber to Float
来源:https://stackoverflow.com/questions/49607492/cast-any-to-float-always-fails-in-swift4-1