Swift 3 类型推断混乱

Swift 3 type inference confusion

我正在使用 macOS。

我有以下代码。 1、2、3、4 和 5 之间的唯一区别是 'metrics' 参数中的内容。

let a = 20
let met = ["a": a]

// 1: This compiles.
_ = NSLayoutConstraint.constraints(withVisualFormat: "|[v1(a)]|", metrics: ["a": 20], views: ["v1": v1])

// 2: This fails with "Cannot convert value of type 'Int' to expected dictionary value type 'NSNumber'".
_ = NSLayoutConstraint.constraints(withVisualFormat: "|[v1(a)]|", metrics: ["a": a], views: ["v1": v1])

// 3: This fails with "Cannot convert value of type '[String: Int]' to expected argument type '[String: NSNumber]?'".
_ = NSLayoutConstraint.constraints(withVisualFormat: "|[v1(a)]|", metrics: met, views: ["v1": v1])

// 4: This compiles.
_ = NSLayoutConstraint.constraints(withVisualFormat: "|[v1(a)]|", metrics: met as [String: NSNumber]?, views: ["v1": v1])

// 5: This fails with "Cannot convert value of type 'Int' to expected dictionary value type 'NSNumber'".
_ = NSLayoutConstraint.constraints(withVisualFormat: "|[v1(a)]|", metrics: ["a": a] as [String: NSNumber]?, views: ["v1": v1])

为什么 1 编译通过,而 2 不编译?

为什么 2 和 3 有不同的错误信息?

为什么4编译通过,5编译不通过?

Swift 3 删除了 Swift 和 Foundation 类型之间的隐式转换(在本例中为 Int -> NSNumber)。 let a = 20a 提供 Int 类型,您需要使用 a as NSNumber 手动将其转换为 NSNumber。另一方面,您的第一行编译是因为 20 推断出预期的 NSNumber 类型。

或者,您可以给 a NSNumber 类型以开始使用

let a: NSNumber = 20

之后您可以在需要 NSNumber 的地方使用它。

我不确定为什么你的第五行没有编译 - 这可能是一个错误。

更新的答案 - 适用于 macOS

使用 Xcode 8 beta 6,Swift 不再隐式桥接 Swift 值类型到基础 class 类型。这意味着如果一个函数需要一个 NSNumber 而你传递给它一个 Int 变量,你将不得不明确地将它转换为 NSNumber。这对于整数文字来说不是必需的,因为 Swift 仍会正确推断类型。

Why does 1 compile, but 2 does not?

1 编译是因为 Swift 能够推断出 20 的类型为 NSNumber,因此 ["a": 20] 可以作为 [String: NSNumber] 使用。

2 无法编译,因为 a 的类型已经确定为 Int,因此您需要将其显式转换为 NSNumber。 Xcode 的修复建议 NSNumber(a),但遗憾的是,它无法编译。使用 NSNumber(value: a)a as NSNumber.

Why do 2 and 3 have different error messages?

对于 2,您提供的是字典文字 ["a": a],因此 Swift 检查每个键和值的类型,看它是否与它期望的字典类型相匹配。因为 a 是一个 Int 并且值是一个 NSNumber,你会得到错误 Cannot convert value of type 'Int' to expected dictionary value type 'NSNumber'。它希望您提供转换。

对于 3,您提供了 [String, Int] 类型的变量。 Swift 告诉您它无法将其转换为 [String, NSNumber]。它可以,但由于 Xcode 8 beta 6.

中的更改,它不能没有显式转换

Why does 4 compile, but 5 does not?

4 可以编译,因为您现在向 [String: NSNumber] 提供了 3 所缺少的显式转换。

5 无法编译,因为您再次提供字典文字,并且 Swift 检查每个键和值以确保它们是正确的类型。如果没有显式转换,它不会将 Int 转换为 NSNumber,因此这里的错误是 无法将类型 'Int' 的值转换为预期的字典值类型 'NSNumber'。关键是 Swift 在将字典文字转换为字典类型时不会转换字典文字的各个键和值。您必须直接为每个演员提供演员表。


上一个答案 - iOS

在 Xcode 8 beta 6 中,参数类型 metrics 已更改为 [String: Any]?。现在,前 4 个示例编译,第 5 个不编译。您的前两个问题不再有效。剩下的唯一问题是:

Why does 4 compile, but 5 does not?

语句 4 (met as [String: NSNumber]) 可以编译,因为 met 的类型为 [String: Int],并且 Swift 可以将 [String: Int] 转换为 [String: NSNumber]。在这种情况下,它正在查看整个字典。 Swift 知道如何将 Int 转换为 NSNumber,但除非您明确要求它这样做,否则它不会这样做。在这种情况下,由于您要呈现 [String: Int] 类型的字典并要求它将其转换为 [String: NSNumber],因此您要求它将 Int 转换为 NSNumber

在语句 5 中,您将字典文字 ["a": a] 转换为字典类型 as [String: NSNumber]。错误信息是:

无法将类型 'Int' 的值转换为预期的字典值类型 'NSNumber'

在这种情况下,Swift 正在查看各个类型,检查 "a"StringaNSNumber .将字典文字转换为类型不会将每个键和值显式转换为相应的类型。在那种情况下,你只是在展示他们并说他们已经是那种人了。由于 Xcode 8 beta 6 的新变化,Swift 将不再隐式地将 Swift 值类型转换为桥接基础类型。所以 Swift 希望您明确地将 Int a 转换为 NSNumber.

有两种方法可以让Swift开心:

["a": NSNumber(value: a)] as [String: NSNumber]
["a": a as NSNumber] as [String: NSNumber]

当然,现在在这两种情况下,字典文字都可以推断为 [String: NSNumber],因此不需要强制转换。

此外,由于 metrics 现在是 [String: Any],因此将 ["a": a] 转换为 [String: NSNumber] 是没有意义的,而 [String: Int] 可以。