Swift中 !和 ?的区别及使用
wyixin
8年前
<h2>开篇</h2> <p>相信大家在学习和使用Swift的时候,肯定会被 ! 和 ? 搞疯过, 纠结这两个符号到底是个什么鬼 ?鬼知道什么时候使用 ! ,什么时候使用 ?</p> <p><img src="https://simg.open-open.com/show/71dcfa3b7e41e38590e5ed818a85272a.jpg"></p> <p>WTF</p> <p>下面就说一下 ! 和 ? 区别以及该怎么使用!</p> <h2>? 和 ! 到底是个啥</h2> <p>? 和 ! 其实分别是Swift语言中对一种可选类型( Optional) 操作的语法糖。</p> <p>那可选类型是干什么的呢? Swift中是可以声明一个没有初始值的属性, Swift中引入了可选类型(Optional)来解决这一问题。它的定义是通过在类型生命后加加一个 ? 操作符完成的。</p> <p>例如: var name: String?</p> <p>Optional 其实是个 enum ,里面有 None 和 Some 两种类型。其实所谓的nil就是 Optional.None , 非nil就是 Optional.Some , 然后会通过 Some(T) 包装(wrap)原始值,这也是为什么在使用 Optional 的时候要拆包(从 enum 里取出来原始值)的原因。这里是enum Optional的定义</p> <pre> <code class="language-swift">enum Optional<T> : LogicValue, Reflectable { case None case Some(T) init() init(_ some: T) /// Allow use in a Boolean context. func getLogicValue() -> Bool /// Haskell's fmap, which was mis-named func map<U>(f: (T) -> U) -> U? func getMirror() -> Mirror }</code></pre> <p>既然这样, 那对于 var name: String? 该怎样去理解这句语法呢?</p> <pre> <code class="language-swift">var name: String? // 上面这个Optional的声明,是”我声明了一个Optional类型值, 它可能包含一个String值,也可能什么都不包含”, 也就是说实际上我们声明的是Optional类型,而不是声明了一个String类型 (这其实理解起来挺蛋疼的...)</code></pre> <p><img src="https://simg.open-open.com/show/e5cf7e415616199f8f592b5c012d0d6a.jpg"></p> <p>搜嘎</p> <h2>? 和 ! 使用</h2> <p>一旦声明为 Optional 的,如果不显式的赋值就会有个默认值 nil 。判断一个 Optional 的值是否有值,可以用if来判断:</p> <pre> <code class="language-swift">if name { // 有值再操作 }</code></pre> <p>怎么使用 Optional 值呢?文档中也有提到说,在使用 Optional 值的时候需要在具体的操作,比如调用方法、属性、下标索引等前面需要加上一个 ? ,如果是 nil 值,也就是 Optional.None ,会跳过后面的操作不执行,如果有值,就是 Optional.Some ,可能就会拆包(unwrap),然后对拆包后的值执行后面的操作,来保证执行这个操作的安全性。</p> <pre> <code class="language-swift">// 例如: let length = name?.characters.count</code></pre> <p>PS:对于 Optional 值,不能直接进行操作,否则会报错。</p> <p>? 的使用场景:</p> <p>1.声明 Optional 值变量</p> <p>2.用在对 Optional 值操作中,用来判断是否能响应后面的操作</p> <p>3.使用 as? 向下转型(Downcast)</p> <p>上面提到 Optional 值需要拆包(unwrap)后才能得到原来值,然后才能对其操作,那怎么来拆包呢?</p> <p>拆包有两种方法:</p> <ul> <li> <p>可选绑定(Optional Binding)</p> <p>可选绑定(Optional Binding)是一种更简单更推荐的方法来解包一个可选类型。 使用可选绑定来检查可选类型的变量有值还是没值。如果有值, 解包它并且将值传递给一个常量或者变量。</p> <img src="https://simg.open-open.com/show/b1a76c0e61f22572f812bf914d5a38ab.jpg"> <p>举栗子</p> </li> </ul> <pre> <code class="language-swift">// 例子最为简单明了 var str: String? = "Hello" let greeting = "World!" if let name = str { let message = greeting + name print(message )} /**自然语言解释意思:就是如果str有值,解包它,并且将它的值赋值给name, 然后执行下面的条件语句; 如果str为空, 直接跳过条件语句块。*/</code></pre> <ul> <li> <p>硬解包</p> <p>硬解包即直接在可选类型后面加一个感叹号(!)来表示它肯定有值。</p> <img src="https://simg.open-open.com/show/21985b4daa0bd0d675dd1df6b99a9ab1.jpg"> <p>再举栗子</p> </li> </ul> <pre> <code class="language-swift">var str1: String? = "Hello" let greeting = "World!" if (str1 != nil) { let message = greeting + str1! print(message) } /**上面例子,我们只是自己知道str1肯定有值, 所以才直接硬解包了str1变量。 但是万一有时候我们的感觉是错的, 那程序在运行时可能会出现严重的错误. 所以Swift中是推荐先检查可选类型是否有值, 然后再进行解包的! */</code></pre> <p>错误示范:</p> <p><img src="https://simg.open-open.com/show/f7e098fd392fac7667a00a5647b7ec8e.jpg"></p> <p>这是:negative_squared_cross_mark: 滴</p> <pre> <code class="language-swift">var str1:String? // str1值可能是传过来的值或者从服务器获取的值 let msg = "Hi" let txt = msg + str1! // runtime error /** 以上代码在编译阶段不会报错.因为使用了硬解包, 编译器认为可选类型是有值的, 所以编译是通过的. 当代码运行起来时, 知名的错误将会出现: `fatal error: Can’t unwrap Optional.None` /*</code></pre> <p>PS:对于 ! 操作符,这里的变量值一定是非nil的!</p> <p>其实, 还有一种叫隐式拆包(Implicitly Unwrapped Optionals),比如 对于会在viewDidLoad进行初始化的变量,可以直接定义为 var str :String! 等于说你每次对这种类型的值操作时,都会自动在操作前补上一个 ! 进行拆包,然后在执行后面的操作,当然如果该值是nil,会报错crash掉。</p> <p>举个很浅显的栗子:</p> <p><img src="https://simg.open-open.com/show/a24e622bc9ac047295ba761fb8aaa992.jpg"></p> <p>最后一个栗子</p> <pre> <code class="language-swift">// 在一个viewController里面,从xib里面拖一个UIImageView控件, 你会发现Xcode会自动给你转成下面的形式 @IBOutlet weak var headerBGImageView: UIImageView! /** 声明Implicitly Unwrapped Optionals值,一般用于类中的属性*/</code></pre> <p>PS:如果你在隐式解析可选类型没有值的时候进行取值,会crash。和在没有值的可选类型里面拆包是一样的。</p> <p>! 的使用场景:</p> <p>1.强制对 Optional 值进行拆包(unwrap)</p> <p>2.声明隐式拆包变量,一般用于类中的属性</p> <h3>结束</h3> <p>其实 ! 和 ? 的问题是很坑的,不要看它仅仅是两个符号,因为只要有一个不小心,不注意,你会发现项目运行起来,会莫名的crash掉了,关键是Debug模式也不是很方便定位错误类型。 自己整理一下关于 可选类型的相关使用,一是记录和巩固所学,而是希望会对大家有所帮助。 本文可能会有错误和不妥之处,还望提出,我会及时改正。</p> <p> </p> <p>来自:http://www.jianshu.com/p/89a2afb82488</p> <p> </p>