摘要:[Swift]Swift中的Optional型別
Optional概念
這是Swift因為安全性考量而設計的,是為了避免在Run-Time時發生的一些常見錯誤。因為不同型別與nil進行運算,例如加減,會出現Run-Time時問題。所以使用Optional,會在編譯時期就觸發錯誤,以早先發現nil的型別運算問題。
宣告Optional變數
Optional的變數是指該變數允許為nil(就是C#中的null),與C#中的nullable 是相同的概念。也就是當宣告變數為Optional後,該變數除了可以設定該型別的值之外,還可以設定為nil。
宣告Optional的語法是,在宣告型別後加上問號?運算子。這樣子在宣告變數的型別之後,即使不給予值,就會自動指派nil給該變數。
var a:String = "Hello "
var b:String?
設定預設值
??運算子是用來判斷該運算子前的算式為nil時,就給予一個預設值。如下例
func checkAge(name:String)-> Int?{
if(name == "aaa"){
return 20;
}else{
return nil;
}
}
var memberAge = checkAge("bbb") ?? 30
使用Optional變數
Unwrap Optional變數

上圖顯示的錯誤訊息是說b變數需要被unwrap後才能與a變數進行運算,概念上是string型別被包裝在Optional型別中,解開包裝(Unwrap)後才是其可以運作的真正型別。那麼要怎麼解開一個Optional型別的變數呢?就是使用!運算子。
var a:String = "Hello "
var b:String? = "aaa"
var c = a+b!
這就是所謂的強迫解除(forced unwrapping)。這是讓開發者自行確認該變數為非nil,所以不進行編譯期檢查。
但如果該變數實際上還是nil值,則在Run-Time時進行運算就會發生錯誤。 
Optional Binding - if let & if var
如果無法確定變數是否為nil就貿然使用! 運算子,當該變數真的為nil時,就會發生Run-Time Error。所以當在執行Optional型別的運算的時候,最好是先判斷是否為nil值再進行運算。
var a:String = "Hello "
var b:String? = "bbb"
if b != nil{
var c = b!
print(a+c)
}
上面的寫法其實有些麻煩,所以Swift提供了一個語法 - 使用if let或if var。如下例所示,意思是如果b變數不是nil,則c變數等於b變數,並執行後面的statement。如果b變數是nil,則不執行該段statement。if let或if var就是所謂的Optional Binding語法
var a:String = "Hello "
var b:String? = "bbb"
if let c = b{
print(a+c)
}
Optional Chaining
當需要存取一個Optional物件中的一個變數時,使用Optional Binding的語法檢查是否為nil,不為nil時才繼續取得該變數。這種方式很直覺,但是如果該變數又是Optional物件時,則又需要使用Optional Binding的語法檢查。
這種多層的Optional物件的檢查,會造成巢狀的結構。同時,最後取得的變數就只能在{}中存活。要使用它還需要另外包一個變數傳出去。所以會有Optional Chaining的語法?.,這語法讓程式碼變得更簡潔。
class Person{
var myHouse: House?
}
class House{
var myRooms: Rooms?
}
class Rooms{
var numberOfDoors = 1
}
var foo = Person()
var num = foo.myHouse!.myRooms!.numberOfDoors
例如上面這個例子,foo.myHouse是Optoinal型別。如果想取得該物件底下的變數,最簡單的方式是透過!直接unwrap。但這種做法在Optoinal物件是nil時,會導致Run-Time Error - fatal error: unexpectedly found nil while unwrapping an Optional value
透過Optional Binding語法,會有這種的巢狀結構
var foo = Person()
if let h = foo.myHouse{
if let r = h.myRooms{
print(r.numberOfDoors)
}
}
對比之下,Optional Chaining的語法?. 讓程式碼變得更簡潔
var num = foo.myHouse?.myRooms?.numberOfDoors
上例透過Optional Chaining回傳的是Int?的型別,只要透過Optional Chaining的方式取得的型別,都會是Optional型別。Optional Chaining應該算是一個運算式,他處理了是否為nil的判斷,並依據實際的物件內容回傳nil或物件值。因為可能回傳nil或實際物件型別,所以這等於是把型別包裝成Optional。
既然是Optional型別,所以也就可以使用Optional Binding語法做是否為nil的判斷
var foo = Person()
if let num = foo.myHouse?.myRooms?.numberOfDoors{
print("可取值 \(num)")
} else {
print("不可取值-nil")
}
Implicitly Unwrapped Optionals
Implicitly Unwrapped Optionals的語法是在宣告時,加上一個驚嘆號!。會有這個型別出現是因為在某些狀況之下,宣告變數初始時會是nil,可是當實際存取時又確定一定會有值。
底下這個連結 - Uses for Implicitly Unwrapped Optionals in Swift 列出幾個需要Implicitly Unwrapped Optionals的理由:
- 當使用MVC的Controller中的@IBOutlet物件時,初始化的時候會是
nil,但是執行時會連結到View的物件 - 在Swift中使用Objective-C API的物件時,其型別是Implicitly Unwrapped Optionals。因為Objective-C物件都是指標,也就代表可能為
nil。
在這情況之下,每次要取用時,都需要Unwrap,其實是滿多餘的動作。可是又不能宣告成一個非Optional的變數,因為他的確一開始的時候會是nil。所以宣告成Implicitly Unwrapped Optional,取值時不需要進行Unwrap,就可以直接使用。
var name:String! = "boo"
print("Hello \(name)")