摘要:[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)")