转载

Swift的多重 Optional

Optional 可以说是 Swift 的一大特色,它完全解决了 “有” 和 “无” 这两个困扰了 Objective-C 许久的哲学概念,也使得代码安全性得到了很大的增加。但是一个陷阱 -- 或者说一个很容易让人迷惑的概念 -- 也随之而来,那就是多重的 Optional。

在深入讨论之前,可以让我们先看看 Optional 是什么。很多读者应该已经知道,我们使用的类型后加上 ? 的语法只不过是 Optional 类型的语法糖,而实际上这个类型是一个 enum:

enum Optional<T> : _Reflectable, NilLiteralConvertible {       case None     case Some(T)      //... }

在这个定义中,对 T 没有任何限制,也就是说,我们是可以在 Optional 中装入任意东西的,甚至也包括 Optional 对象自身。打个形象的比方,如果我们把 Optional 比作一个盒子,实际具体的 String 或者 Int 这样的值比作糖果的话,当我们打开一个盒子 (unwrap) 时,可能的结果会有三个 -- 空气,糖果,或者另一个盒子。

空气和糖果都很好理解,也十分直接。但是对于盒子中的盒子,有时候使用时就相当容易出错。特别是在和各种字面量转换混用的时候需要特别注意。

对于下面这种形式的写法:

var string: String? = "string"   var anotherString: String?? = string

我们可以很明白地知道 anotherString 是 Optinal<Optional<String>>。但是除开将一个 Optional 值赋给多重 Optional 以外,我们也可以将直接的字面量值赋给它:

var literalOptional: String?? = "string"

这种情况还好,根据类型推断我们只能将 Optional<String> 放入到 literalOptional 中,所以可以猜测它与上面提到的 anotherString 是等效的。但是如果我们是将 nil 赋值给它的话,情况就有所不同了。考虑下面的代码:

var aNil: String? = nil  var anotherNil: String?? = aNil   var literalNil: String?? = nil

anotherNil 和 literalNil 是不是等效的呢?答案是否定的。anotherNil 是盒子中包了一个盒子,打开内层盒子的时候我们会发现空气;但是 literalNil 是盒子中直接是空气。使用中一个最显著的区别在于:

if let a = anotherNil {       print("anotherNil") }  if let b = literalNil {       print("literalNil") }

这样的代码只能输出 anotherNil。

另一个值得注意的地方时在Playground 中运行时,或者在用 lldb 进行调试时,直接使用 po 指令打印 Optional 值的话,为了看起来方便,lldb 会将要打印的 Optional 进行展开。如果我们直接打印上面的 anotherNil 和 literalNil,得到的结果都是 nil:

(lldb) po anotherNil nil  (lldb) po literalNil nil

如果我们遇到了多重 Optional 的麻烦的时候,这显然对我们是没有太大帮助的。我们可以使用 fr v -R 命令来打印出变量的未加工过时的信息,就像这样:

(lldb) fr v -R anotherNil (Swift.Optional<Swift.Optional<Swift.String>>)     anotherNil = Some {     ... 中略 } (lldb) fr v -R literalNil (Swift.Optional<Swift.Optional<Swift.String>>)     literalNil = None {     ... 中略 }

这样我们就能清晰地分辨出两者的区别了。

原文  http://blogread.cn/it/article/7610?f=hot1
正文到此结束
Loading...