在 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
填充吗?
Tested with Xcode Playground 7.2 + Swift 2.1.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
的变量能够保存枚举值(或任何其他类型的值,甚至是元类型信息),因此它应该能够保存例如 nil
。字符串,又名 String?.none
, 又名 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
通用参数和普通参数 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
它应该分配。我们可以将关键字视为提供糖语法。
关于swift - 为什么非可选的 Any 可以容纳 nil?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34644128/