在Swift4.1中,将任何类型强制转换为Float类型总是失败的。

9
在之前的版本中,我可以使用 let float = dict["somekey"] as? Float 从一个 [String: Any] 字典中获取一个浮点值,但在Swift4.1中,它不再起作用。似乎在我获取它之前,dict["somekey"] 的类型已被隐式推断为Double,因此从Double转换为Float总是失败的。我想知道这是一个新特性还是一个错误。
--以下是更新内容。
我重新下载了Xcode9.2并进行了一些实验,现在我想我明白了发生了什么。这是测试代码:
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")
}

在Swift4和Swift4.1的Playground中运行此代码,结果不同。在Swift4中,结果为nil 0.2 0.3 0.4,而在Swift4.1中,结果为nil nil nil nil。从结果可以看出两点: 1. 当我们使用JSONSerialization类将JSON数据转换为[String:Any]字典时,数值会保存为NSNumber对象,而不是IntDoubleFloat。 2. 在Swift4中,我们可以使用let float = NSNumberOjbect as? Float来获取Float值,但在Swift4.1中我们不能。但是,在Swift4或Swift4.1中仍然可以以这种方式获取IntDouble值。 最后,这是一个新特性还是一个错误?如果有人知道,请给出公告链接吗?

编译器是否表示它总是会失败?还是你通过多次运行代码发现它总是失败的? - Sweeper
行为已经在Swift 3中找到,当dict是Swift本地的[String: Any]时。当一个NSDictionary被导入为[String: Any]时,行为是不同的。 - OOPer
一行代码,例如 guard let float: Float = dict["key"] as? Float else { return },可以在Swift4.0中获取一个浮点数值,但在Swift4.1中退出。 - LYM
请查看以下链接以获取解决方案:https://dev59.com/LKHia4cB1Zd3GeqPQDhl#43623573 - Ankit Jayaswal
关于您更新的部分,请查看此链接:NSNumber桥接和数字类型。该文章展示了该特性在Swift 4中的实现,但是在Swift 4中实现似乎不完整,而Swift 4.1则显示正确的行为。请尝试使用值0.50.25进行样例代码测试,这些值可以精确表示为Float - OOPer
显示剩余3条评论
1个回答

10
你需要区分两种情况(在Swift 3中有三种情况):
  • Any 包含一个本地的 Swift Double
  • Any 包含一个 NSNumber

(在Swift 3中,除了常规的 NSNumber,还有保持类型的 NSNumber 情况。)


当你创建一个本地的 Swift Dictionary,例如 [String: Any],并以正常方式设置 Double 值,如在你的更新中:

let dict: [String : Any] = ["key": 0.1]
在这种情况下,Any 包含代表 Double 和原始值 0.1 (作为 Double)的元数据。
将包含 DoubleAny 强制转换为 Float 总是失败的。因为 DoubleFloat 无法通过 as 强制转换进行转换。
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

然而,如果Any持有NSNumber,当从NSArrayNSDictionary桥接到ArrayDictionary时(与JSON序列化结果相匹配),前几个版本的Swift和Swift 4.1之间的行为是不同的。在以前的Swift版本中,(正常情况下)NSNumber转换为Float始终是成功的操作。而在Swift 4.1中,这种行为发生了变化,具体可以查看以下链接:SE-0170 NSNumber bridging and Numeric types。解决Any转换为Float问题的关键在于获取DictionaryArray的方式以及如何向它们设置值。

最后,这是一个新功能,而不是一个错误,相关讨论可以参考以下链接:Unable to bridge NSNumber to Float Swift 3.3Unexpected behavior when casting an NSNumber to Float


如果 ["key": 0.1] 转换为浮点数失败,但是 ["key": 1.0] 成功了,这背后可能的原因是什么? - Mehul Thakkar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接