Swift 3类型的推理混淆

时间:2016-08-19 21:50:48

标签: swift macos swift3

我正在使用macOS。

我有以下代码。 1,2,3,4和5之间的唯一区别在于'指标'参数。

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不编译?

2 个答案:

答案 0 :(得分:4)

更新的答案 - 对于macOS

使用Xcode 8 beta 6,Swift不再隐式地将Swift值类型桥接到Foundation类类型。这意味着如果函数期望NSNumber并且您将Int变量传递给它,则必须将其明确地转换为NSNumber。这对于整数文字不是必需的,因为Swift仍然会正确地推断出类型。

  

为什么1编译,但2不编译?

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

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

  

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

对于2,您提供了一个字典文字["a": a],因此Swift会检查每个键和值的类型,以查看它是否与它所期望的字典类型相匹配。由于aInt且值为NSNumber,因此您会收到错误无法转换类型' Int'预期字典值类型' NSNumber' 。它希望您提供转换。

对于3,您提供的类型为[String, Int]的变量。 Swift告诉你,它无法将其转换为[String, NSNumber]。由于Xcode 8 beta 6的变化,它可以,但不是没有明确的演员。

  

为什么4编译,但5不编译?

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

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

上一个答案 - 适用于iOS

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

  

为什么4编译,但5不编译?

声明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]是没有意义的。

答案 1 :(得分:1)

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

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

let a: NSNumber = 20

之后您可以在预期NSNumber的地方使用它。

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