Optional(可選類型)
Optional
是Swift中引入的新類型. 表示可以有值也可以沒值. 當它沒值時, 就是nil
. Optional類型其實是枚舉:
enum Optional<T> : Reflectable, NilLiteralConvertible {
case None
case Some(T)
init()
init(_ some: T)
/// Haskell's fmap, which was mis-named
func map<U>(f: (T) -> U) -> U?
func getMirror() -> MirrorType
static func convertFromNilLiteral() -> T?
}
當Optional
沒有值時,返回的nil
其實就是Optional.None
,即沒有值。除了None
以外,還有一個Some
,當有值時就是被Some<T>
包裝的真正的值,所以我們拆包的動作其實就是將Some
里面的值取出來。
Swift和OC中nil的區別
- OC中,
nil
是一個指向不存在對象的指針; - Swift中,
nil
不是指針,它是一個確定的值,用于表示值缺失。任何類型的可選狀態都可以設置為nil
,不只是對象類型(當基礎類型(整形、浮點、布爾等)沒有值時,也是nil
);
定義
定義一個Optional的值只需要在類型后面加上問號(?),如:
var str: String?
另外:在上面可以看到,Optional其實就是一個枚舉,然后給它指定一個類型就行了,所以下面這兩種方法都能聲明一個Optional值:
var str: String! = "Hello World!"
var str2: Optional<String>
一個Optional值和非Optional值的區別就在于:Optional值未經初始化雖然為nil,但普通變量連nil都沒有:
//未被初始化,但是是一個Optional類型,為nil
var str: String?
str //輸出nil
//未被初始化,也不是Optional類型
var str2: String
str2 //使用時出錯
使用
關于! 和 ? 使用場景
?的使用場景:
1)聲明Optional值變量
2)在對Optional值操作中,用來判斷是否能響應后面的操作
!的使用場景:
1)強制對Optional值進行拆包
2)聲明隱式拆包變量,一般用于類中的屬性
**對于可選類型, 使用之前需要拆包才不會報錯. 拆包有兩種方式. **
顯式拆包
- Optional Binding
正常情況下,
let pet = jackon.pet
的返回值不是Bool
,而是“有值”或者“沒有值”,不能直接用于條件表達式。但是if let
的搭配是一種僅僅針對Optional
類型的特殊的情況,蘋果有意在編譯器做了這種處理。
if let str = strValue {
let hashValue = str.hashValue
}
-
通過! str!
對比拆包前后,對str的輸出:var str: String? = "Hello World!"
str //{Some "Hello World!"}
str! //Hello World!
隱式拆包
通過在聲明時的數據類型后面加一個感嘆號(!)來實現:
var str: String! = "Hello World!"
str //Hello World!
可以看到沒有使用(?)進行顯式的折包也得到了Some
中的值,這個語法相當于告訴編譯器:在我們使用Optional
值前,這個Optional
值就會被初始化,并且總是會有值,所以當我們使用時,編譯器就幫我做了一次拆包。如果你確信你的變量能保證被正確初始化,那就可以這么做,否則還是不要嘗試為好。
Optional Chaining
當一個Optional
值調用它的另一個Optional
值的時候,Optional Chaining
就形成了,基本上,Optional Chaining
就是總是返回一個Optional
的值,只要這個Chaining
中有一個值為nil
,整條Chaining
就為nil
,和Objective-C
的向nil
發消息類似。
//不使用Optional Chaining需要判斷兩次
if let pet = jackon.pet {
if let toy = pet.favoriteToy {
toy.name
}
}
//使用Optional Chaining只需要判斷一次
if let toy = jackon.pet?.favoriteToy {
toy.name
}
Optional Chaining
除了能將屬性返回的類型變為Optional
外,連方法的返回值都能強制變為Optional
,哪怕這個方法沒有返回值,但是別忘了,Void
也算是一個類型:
typealias Void = ()
如果Pet類有一個玩玩具的play方法的話,就可以這樣來判斷是否會調用成功:
if let p: Void = jackon.pet?.play() {
"play is called"
}