原文出處:Understanding Optionals in Swifthtml
蘋果新的Swift編程語言帶來了一些新的技巧,能使軟件開發比以往更方便、更安全。然而,一個頗有力的特性Optional,在你第一次使用時可能會感到困惑。Optionals將會在編譯階段檢查哪些值爲nil。經過這種方式,你能夠更好的保證應用程序交付在用戶手裏是可運行的。在Swift中,Optionals也提供了一些接口用來和遺留的Objective-C代碼之間交互。react
初試Optional編程
讓咱們在XCode中新建一個叫作swift-optionals的playground文件。你能夠添加下面的代碼來看看Optionals是什麼樣的。swift
import Foundation var rightURL = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io} var wrongURL = NSURL(string: "this is not a real url") // => nil
在這種場景下,咱們試着經過字符串來建立NSURL。對於rightURL,咱們獲得了一個Some的結果,裏面存放着一個NSURL對象,對於wrongURL,咱們將獲得nil。在兩種狀況下都沒有直接獲得NSURL,這是一件好事,由於Some結果須要顯示的解包,這將迫使咱們檢查nil值,讓咱們看看這是如何工做的。安全
一切都是Someapp
若是咱們在XCode中看下NSURL的構造函數,咱們將會看到以下的代碼:編程語言
convenience init?(string URLString: String)
那個在init以後的問號標記告訴咱們當構造函數執行完畢,NSURL將會返回一個Optional。在Swift中,Optional是一個實際的類型,更加明確的說是一個泛型的enum,這種類型或者持有另外一個對象,或者是nil。讓咱們看看Optional長什麼樣?函數
enum Optional<T> : Reflectable, NilLiteralConvertible { case None case Some(T) /// Construct a `nil` instance. init() /// Construct a non-\ `nil` instance that stores `some`. init(_ some: T) ...
咱們可以看到Optional枚舉包括兩個case,None表明nil,Some表明具體的泛型類型。在Swift中,咱們能夠把任何東西裝箱在Optional.Some中。工具
Optional<String>.Some("really good stuff") // => {Some "really good stuff"}
另外一方面,None等價於nilthis
Optional<String>.None == nil // => true
從這個角度來說,你能夠把Optional理解爲一個箱子,有可能會包含一個具體的類型,也有可能沒有包含。向一個方法或者函數發送一個箱子類型而不是具體類型,編譯器將會強迫你打開箱子檢查箱子裏面的類型。若是箱子是空的,你能夠捕捉這個錯誤而且處理這種錯誤。
Swift將會推斷出變量的類型,不過爲了讓事情變得更透明,咱們顯示的以兩種方式來使用Optional:
import Foundation var rightURL: Optional<NSURL> = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io} var wrongURL: NSURL? = NSURL(string: "this is not a real url") // => nil
對於rightURL,咱們經過一種冗長的方式來建立一個Optional類型,由於咱們會大量使用Optional,Swift給了咱們一種簡寫方式,經過在變量後面追加一個問號‘?',就如同咱們以前看到的NSURL構造器那樣。可是若是咱們在NSURL變量後面去掉問號會怎麼樣?
import Foundation var cheatURL: NSURL = NSURL(string: "http://www.reactive.io")
咱們將會獲得一個編譯錯誤」Value of optional type ‘NSURL?’not unwrapped; did you mean to use ‘!’or ‘?’」。咱們已經知道'?'是幹嗎的了,那麼感嘆號標記'!'是幹嗎的呢?
隱式和顯示Optional
在一個類型後面使用'?'用來顯示代表這是一個Optional,讓咱們使用'!'看看會發生什麼:
import Foundation var implicitURL: NSURL! = NSURL(string: "http://www.reactive.io/tips") // => http://www.reactive.io/tips
此次咱們獲得了一個沒有被Some包裝的NSURL。使用'!'使得implicitURL看起來跟rightURL這個optional差很少,用'!'標記的類型實爲ImplicitlyUnwrappedOptional,當你使用值的時候Swift編譯器將會自動爲你展開裏面的值。使用ImplicitlyUnwrappedOptional類型會帶來危險,由於編譯器不會迫使咱們處理值爲nil的狀況。可是初始化爲nil會幫咱們跟遺留的Objective-C代碼之間搭起橋樑。
下面是4中不一樣的Optional使用方式:
import Foundation var rightURL: Optional<NSURL> = NSURL(string: "http://www.reactive.io") // => {Some http://www.reactive.io} var wrongURL: NSURL? = NSURL(string: "this is not a real url") // => nil var implicitURL: NSURL! = NSURL(string: "http://www.reactive.io") // => http://www.reactive.io/tips var explicitURL: ImplicitlyUnwrappedOptional<NSURL> = NSURL(string: "this is another bad url") // => nil
如何使用Optional
第一種方法是顯示檢查Optional是不是nil:
if rightURL != nil { println("got a URL: \(rightURL)") // => "got a URL: Optional(http://www.reactive.io)" } else { println("no URL, sorry :(") }
在上面獲得了Optional(http://www.reactive.io),這並非咱們想要的,咱們想獲得的是Optional裏面包含的內容。如何作到呢?靠'!'符號,在變量或常量後面追加'!'符號將會展開Optional裏面的值,若是是nil值將會拋出異常,讓咱們試試:
if rightURL != nil { println("got a URL: \(rightURL!)") // => "got a URL: http://www.reactive.io" } else { println("no URL, sorry :(") }
咱們能夠經過另外一種方式if let塊來實現:
if let url = rightURL { println("got a URL: \(url)") // => "got a URL: http://www.reactive.io" } else { println("no URL, sorry :(") }
你也能夠經過switch語句來實現:
switch rightURL { case nil: println("no URL, sorry :(") default: println("got a URL: \(rightURL!)") }
你還可使用??操做符進行鏈式調用獲得不爲nil的值:
var myURL = wrongURL ?? explicitURL ?? rightURL // {Some http://www.reactive.io} println("got a URL: \(myURL!)") // => "got a URL: http://www.reactive.io"
編寫一個類
是時候來看看如何在面向對象的代碼中使用Optional類型了,複製下面的代碼到playground中:
import Foundation class Person { var name: String var address: String init(name: String, address: String) { self.name = name self.address = address } func description() -> String { return "\(name) @ \(address)" } } class Box { var contents: String var sender: Person! var recipient: Person? init(contents: String) { self.contents = contents } } var alice = Person(name: "Alice", address: "New York City") var book = Box(contents: "A Good Book") book.sender = alice // => {name "Alice" address "New York City"}
注意在Box類中,sender屬性是一個ImplicitlyUnwrappedOptional類型,recipient屬性是Optional類型。不過如果將這兩個類型換爲普通的Person類型,Swift編譯器將會報出一個錯誤。由於這兩個屬性並無在構造函數中賦值,因此這兩個屬性在構造函數調用的時候沒有被初始化。在上面的例子中,book被初始化後,sender和recipient都默認爲nil,可是咱們肯定book必定有一個sender,因此sender爲ImplicitlyUnwrappedOptional類型,在例子中,sender爲alice.可是不必定有recipient,因此別人在使用book對象的時候須要檢查recipient是否有值。
方法調用
若是咱們想要獲得sender或者recipient的description,咱們也許會獲得一些麻煩。這是由於咱們不能在nil值上調用description方法,另外使用!強制展開nil值Optional還會拋出異常。使用if else條件表達式調用方法會顯得很繁瑣。Swift提供了另外一個工具,經過使用?操做符來進行鏈式調用。在調用方法的時候先檢查值是不是nil:
book.sender?.description() // => {Some "Alice @ New York City"} book.recipient?.description() // => nil
咱們使用了一種真確的方法調用了description方法,更進一步咱們獲得了一個Optional類型:
book.recipient = Person(name: "Bob", address: "San Francisco") if let note = book.recipient?.description() { println("Hey \(note), enjoy the Book!") // => "Hey Bob @ San Francisco, enjoy the Book!" }
總結
本文說了一些關於Swift中Optionals的事情,這將幫助你在寫代碼的時候更好的用上它,而且在使用類庫的時候使用它們。熟練的使用不一樣方式的Optional將會使你保證你寫代碼更迅速,減小運行時的錯誤。