在Swift中,我可以声明一个类型常量Any并将其String放入。
Any
String
let any: Any = "hello world"
好。另一方面,我 不能 输入nil值,any因为它不是可选的。
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的测试
TL; DR; swift中的可选项由编译器转换为Optional枚举实例,并且由于Any可以映射到任何值,因此可以用于存储可选项。
Optional
Swift如何表示可选内容?它通过映射SomeType?到Optional枚举的具体实现来实现:
SomeType?
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。
String?.none
Optional<String>.none
让我们看看会发生什么。正如Optional声明所看到的,它nil对应.none于所有类型的枚举情况:
.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,另一个?
T
Int
这给出了支持以上段落的错误消息:
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。
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应该分配哪种类型。我们可以认为该关键字提供了糖语法。
Optional<T>.none