升级到 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 Generics
和 Optionals
行为相关的奇怪行为。我们将 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 的发行说明,但没有发现关于可选或泛型的任何更改。真的很难理解是什么导致了版本之间的这种行为变化。
特别有趣的是 phone2
和 phone3
的行为有何不同。上面的代码示例中几乎没有发生奇怪的事情,所以问题是是否有人知道是什么导致了此版本中的行为变化?
这是由于 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)
传递给参数并且解包成功。
但是我会质疑您为什么首先要处理双重包装的可选值——除非绝对必要,否则我强烈建议避免使用它们。
我们刚刚将 Xcode 更新到 10.2(因此 iOS 12.2 SDK)并开始看到与 Swift Generics
和 Optionals
行为相关的奇怪行为。我们将 Swift 版本保持在 4.2,因此没有 Swift 5 更新。唯一的变化是从 Xcode 10.1.
这是一个示例代码,说明了这些奇怪之处。注释显示了版本之间的变化。理想情况下,不应有任何更改。
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 的发行说明,但没有发现关于可选或泛型的任何更改。真的很难理解是什么导致了版本之间的这种行为变化。
特别有趣的是 phone2
和 phone3
的行为有何不同。上面的代码示例中几乎没有发生奇怪的事情,所以问题是是否有人知道是什么导致了此版本中的行为变化?
这是由于 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)
传递给参数并且解包成功。
但是我会质疑您为什么首先要处理双重包装的可选值——除非绝对必要,否则我强烈建议避免使用它们。