一尘不染

为什么非可选的Any可以保持nil?

swift

在Swift中,我可以声明一个类型常量Any并将其String放入。

let any: Any = "hello world"

好。另一方面,我 不能 输入nil值,any因为它不是可选的。

let any: Any = nil

error: nil cannot initialize specified type 'Any' (aka 'protocol<>')
let any: Any = nil
              ^

完善。但是为什么编译器允许我编写以下代码?

let couldBeNil: String? = nil
let any: Any = couldBeNil
print(any) // nil

是否不Any遵循Swift规则,只能使用Optional var / let填充nil

经过Xcode Playground 7.2 + Swift 2.1.1的测试


阅读 202

收藏
2020-07-07

共1个答案

一尘不染

TL; DR; swift中的可选项由编译器转换为Optional枚举实例,并且由于Any可以映射到任何值,因此可以用于存储可选项。


Swift如何表示可选内容?它通过映射SomeType?Optional枚举的具体实现来实现:

Int? => Optional<Int>
String? => Optional<String>

简化的Optional外观声明如下:

enum Optional<T> {
    case none    // nil
    case some(T) // non-nil
}

现在,类型变量Any可以保存枚举值(或任何其他类型的值,甚至是元类型信息),因此它应该可以保存例如nilString,aka
String?.none,aka Optional<String>.none

让我们看看会发生什么。正如Optional声明所看到的,它nil对应.none于所有类型的枚举情况:

nil == Optional<String>.none // true
nil == Optional<Int>.none    // true
[Double]?.none == nil        // also true

因此,从理论上讲,您应该能够分配nil给声明为的变量Any。尽管如此,编译器仍不允许这样做。

但为什么不编译器让你指定nil一个Any变量?这是因为它无法推断要映射.none枚举大小写的类型。Optional是一个通用枚举,因此它需要一些东西来填充T通用参数,而plain
nil太宽泛了。.none应该使用哪个值?一个来自Int,一个来自String,另一个?

这给出了支持以上段落的错误消息:

let nilAny: Any = nil // error: nil cannot initialize specified type 'Any' (aka 'protocol<>')

以下代码有效,并且等效于分配一个nil

let nilAny: Any = Optional<Int>.none

,因为上述Any变量实际上持有Optional枚举的有效值。

间接分配也起作用,因为幕后将nil其转换为Optional<Type>.none

var nilableBool: Bool? // nilableBool has the Optional<Bool>.none value
var nilBoolAsAny: Any = nilableBool // the compiler has all the needed type information from nilableBool

与其他语言不同,Swift
nil对应于一个具体值。但是它需要一种类型,以便编译器知道Optional<T>.none应该分配哪种类型。我们可以认为该关键字提供了糖语法。

2020-07-07