String 和 String! 不相同。语言中恰好有足够的糖在它们之间进行转换。同样,语言中有糖可以在String 和String? 之间进行转换(但不能反过来)。
从基础开始。有String。除非有一些强有力的理由,否则当您指的是字符串时,您应该使用String。其他一切都是“更多的东西”,除非你需要,否则你不应该添加它。
有Optional<String>。这只是一个有两种情况的枚举,一种有值,一种没有值:
public enum Optional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
// ...
}
Optional 有一个后缀运算符 !,如果可用则返回 Wrapped,如果不可用则崩溃。到目前为止,还没有魔法。这是您可以自己构建的东西。
Optional 周围很少有魔法。首先,类型Wrapped? 被编译器神奇地转换为Optional<Wrapped>(对于任何Wrapped)。这只是语法糖。这两个符号是相同的。其次,?.“运算符”存在可选链接(它不是真正的运算符;它是语言的一部分,你不能自己构建它)。然后是可选的促销。如果需要,任何类型的Wrapped 都可以自动隐式转换为Wrapped?。 Optional 周围几乎没有其他魔法,例如 if-let 语法,还有 nil 是 Optional.None 的同义词(我相信它们实际上是相同的)。但Optional 确实只是一个泛型类型,实现为枚举。它只是编译器知道的特殊类型。
然后是ImplicitlyUnwrappedOptional<Wrapped>。这也只是一个枚举(在 Swift 2.2 中;这将在 Swift 3 中改变)。
public enum ImplicitlyUnwrappedOptional<Wrapped> : _Reflectable, NilLiteralConvertible {
case None
case Some(Wrapped)
// ...
}
这与Optional 不同,也与Wrapped 不同。这是一个完全不同的类型。但它也有一些与之相关的魔法。首先,Wrapped! 类型是ImplicitlyUnwrappedOptional<Wrapped> 的语法糖。再说一次,这只是糖。两者是相同的(在 Swift 2.2 中,而不是在 Swift 3 中)。接下来,如果IUO<Wrapped>出现在期望Wrapped的地方,则会自动转换为Wrapped,如果没有值则crash。如果在需要Wrapped? 的地方找到它,它将自动转换为Wrapped?。这些很神奇,这就是为什么有时String 和String! 似乎 是同一类型。这只是编译器通过添加一个不可见的转换步骤神奇地为您“使其工作”。这并不意味着它们真的是同一类型。
IUO 在桥接某些 Objective-C 模式时非常有用,尤其是涉及 Storyboard 时,应避免在这些情况之外使用。即使在这些情况下,IUO 也只是为了方便。你可以使用常规的 Optionals 来做所有相同的事情,你只需要更频繁地使用if-let 检查它们。使用 Optionals 比使用 IUO 安全得多。很容易想到“我肯定知道这个值总是在使用之前就设置好了”。就在这周,我追赶了一个撞车者,因为这件事是错误的。 “它应该是”和“它必须是”之间是有区别的。但是,在 Storyboard 中使用 Optional 完全安全可能会非常不方便,并且可能会掩盖一些错误(通过什么都不做而不是崩溃),所以这是 IUO 最常见的地方。
IUO 属性曾经对处理失败的init 方法很有价值。这在 Swift 2.2 中不再是问题,所以这种使用已经消失了。我遇到的最后一个“纯 Swift”使用是当你必须将 self 传递给你作为属性存储的东西的初始化程序时(此时你不能传递 self,因为你的所有属性都已初始化)。这是一个不幸的用例,非常混乱,我希望我们能找到解决办法。在这些情况之外,您应该避免隐式展开的 Optional。