Swift中 !和 ?的区别及使用

wyixin 7年前
   <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>