【问题标题】:Swift 3 type inference confusionSwift 3 类型推理混淆
【发布时间】:2016-12-27 03:40:03
【问题描述】:

我正在使用 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 macos swift3


    【解决方案1】:

    更新答案 - 适用于 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 的 fix-it 建议 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 可以编译,因为您现在正在向 [String: NSNumber] 提供 3 所缺少的显式转换。

    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 值类型转换为桥接的 Foundation 类型。所以 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] 可以。

    【讨论】:

    • 我正在使用 Xcode8b6。指标参数的类型是'[String: NSNumber]?'。这些错误消息来自 Xcode 8b6,因此所有问题都是有效的。
    • 你是在 macOS 上开发吗?看来iOS和macOS是不一样的。
    • 是的,我应该提到这一点。我在 macOS 上。我不知道 iOS 和 macOS 是不同的。
    • 我想我还是很困惑。当字典文字和字典变量具有相同类型时,为什么在行为上存在差异?为什么文字与变量的选择很重要?在 2/3 中,两者都具有类型“[String: Int]”。而在 4/5 中,两者都具有类型 '[String: Int]' 和相同的显式转换为 '[String: NSNumber]?'。
    • 对于字典字面量,Swift 还没有确定字面量的类型。考虑["a": 0]。是[String: Int] 还是[String: Double]。如果你将它分配给一个变量,Swift 将不得不选择一个,它默认为[String: Int],但如果你将该文字传递给一个接受[String, Double] 的函数,它将决定将0 视为@987654378 @。它不会将Int 转换为Double,而是确定0 是有效的Double
    【解决方案2】:

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

    或者,您可以将a 指定为NSNumber 类型以开始使用

    let a: NSNumber = 20
    

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

    我不确定为什么您的第五行无法编译 - 这可能是一个错误。

    【讨论】:

      猜你喜欢
      • 2015-11-29
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-31
      • 1970-01-01
      相关资源
      最近更新 更多