升级到 iOS 12.2 (Xcode 10.2) 后 Swift 4.2 中的奇数泛型和可选行为

Odd Generics & Optional Behavior in Swift 4.2 after upgrading to iOS 12.2 (Xcode 10.2)

我们刚刚将 Xcode 更新到 10.2(因此 iOS 12.2 SDK)并开始看到与 Swift GenericsOptionals 行为相关的奇怪行为。我们将 Swift 版本保持在 4.2,因此没有 Swift 5 更新。唯一的变化是从 Xcode 10.1.

更新到 Xcode 10.2

这是一个示例代码,说明了这些奇怪之处。注释显示了版本之间的变化。理想情况下,不应有任何更改。

class Phone<T> {}

extension Phone {
    class func create(initial: T? = nil) -> Phone<T> {
        if let _ = initial { print("Regular: Unwrapping worked.") }
        return Phone()
    }
}

extension Phone where T == Void {
    class func create(initial: T? = nil) -> Phone<T> {
        if let _ = initial { print("T == Void: Unwrapping worked.") }
        return Phone()
    }
}

var phone: Phone<Int?> = Phone()
var phone2: Phone<Int?> = Phone()
var phone3: Phone<Int?> = Phone()

// unwrapping works iOS 12.1, doesn't work in 12.2
phone = Phone.create(initial: Optional(nil))

// unwrapping works iOS 12.1, doesn't work in 12.2
phone2 = Phone.create(initial: Optional<Int?>(nil))

// doesn't compile in iOS 12.1, unwrapping works in iOS 12.2
phone3 = Phone.create(initial: Optional<Int>(nil))

// doesn't compile in iOS 12.1, unwrapping doesn't work in 12.2 (uses the T == Void function)
let phone4 = Phone.create(initial: Optional(nil))

我们已经阅读了 Xcode 10.2 的发行说明,但没有发现关于可选或泛型的任何更改。真的很难理解是什么导致了版本之间的这种行为变化。

特别有趣的是 phone2phone3 的行为有何不同。上面的代码示例中几乎没有发生奇怪的事情,所以问题是是否有人知道是什么导致了此版本中的行为变化?

这是由于 SE-0213: Literal initialization via coercion,这意味着 Optional(nil) 现在被编译器视为 nil as Optional。以前使用 Optional(nil),你会得到一个 wrapped nil 值,例如 Int??.some(nil),但现在你只得到 nil.

因此对于以下内容:

let phone: Phone<Int?> = Phone.create(initial: Optional(nil))

编译器将其视为:

let phone: Phone<Int?> = Phone.create(initial: nil as Optional)

相当于:

let phone: Phone<Int?> = Phone.create(initial: nil)

因为您已将通用参数 T 指定为 Int?,所以 initial: 参数采用 Int??。因此,传递 nil 就是传递 Int??.none,因此解包失败。

恢复旧行为的一种方法是显式指定 .init 以强制编译器调用初始化程序:

let phone: Phone<Int?> = Phone.create(initial: Optional.init(nil))

现在您将 Int??.some(nil) 传递给参数并且解包成功。

但是我会质疑您为什么首先要处理双重包装的可选值——除非绝对必要,否则我强烈建议避免使用它们。