Swift可選值(Optional Values)介紹:?和!使用總結(jié)

一、Optional的定義

Optional是蘋果引入到Swift語言中的全新類型,是Objective-C語言中沒有的數(shù)據(jù)類型。它的特點(diǎn)就和它的名字一樣:可以有值,也可以沒有值,當(dāng)它沒有值時(shí),就是nil。

注意Swift和OC中nil的區(qū)別

  • OC中,nil是一個(gè)指向不存在對象的指針(只有對象才能為nil);
  • Swift中,nil不是指針,它是一個(gè)確定的值,用于表示值缺失。任何類型的可選狀態(tài)都可以設(shè)置為nil,不只是對象類型(當(dāng)基礎(chǔ)類型如整形、浮點(diǎn)、布爾等沒有值時(shí),也是nil)。沒有初始值的值,是不能使用的,這就產(chǎn)生了Optional類型。

定義一個(gè)Optional的值很容易,只需要在類型后面加上問號(?)就行了。

//未被初始化,但是是一個(gè)Optional類型,為nil
var str: String?
str //輸出nil
//未被初始化,也不是Optional類型
var str2: String
str2    //使用時(shí)出錯(cuò)

一個(gè)Optional值和非Optional值的區(qū)別就在于:Optional值未經(jīng)初始化雖然為nil,但普通變量連nil都沒有。

var number: Int? = 32

等價(jià)于

var numbet: Optional<Int> = 32

一個(gè)Optional對象只存在兩種狀態(tài):包含一個(gè)值,或者為空,我們都可以通過解包(unwrap)來獲取

解包使用的原因
Optional類型其實(shí)是一個(gè)枚舉:

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?
}

當(dāng)Optional沒有值時(shí),返回的nil其實(shí)就是Optional.None,即沒有值。除了None以外,還有一個(gè)Some,當(dāng)有值時(shí)就是被Some<T>包裝的真正的值,所以我們拆包的動作其實(shí)就是將Some里面的值取出來。

關(guān)于! 和 ? 使用場景

?的使用場景:

  • 1)聲明Optional值變量
  • 2)在對Optional值操作中,用來判斷是否能響應(yīng)后面的操作

!的使用場景:

  • 1)強(qiáng)制對Optional值進(jìn)行拆包
  • 2)聲明隱式拆包變量,一般用于類中的屬性

對于可選類型, 使用之前需要拆包才不會報(bào)錯(cuò)。拆包有兩種方式:

  • 顯式拆包
  1. Optional Binding
    正常情況下,let pet = jackon.pet 的返回值不是 Bool,而是“有值”或者“沒有值”,不能直接用于條件表達(dá)式。但是 if let 的搭配是一種僅僅針對 Optional 類型的特殊的情況,蘋果有意在編譯器做了這種處理。
if let str = strValue {
   let hashValue = str.hashValue
}
  1. 通過! str!
    對比拆包前后,對str的輸出:
var str: String? = "Hello World!"
str     //{Some "Hello World!"}
str!    //Hello World!
  • 隱式拆包
    通過在聲明時(shí)的數(shù)據(jù)類型后面加一個(gè)感嘆號(!)來實(shí)現(xiàn):
var str: String! = "Hello World!"
str //Hello World!

可以看到?jīng)]有使用(?)進(jìn)行顯式的折包也得到了Some中的值,這個(gè)語法相當(dāng)于告訴編譯器:在我們使用Optional值前,這個(gè)Optional值就會被初始化,并且總是會有值,所以當(dāng)我們使用時(shí),編譯器就幫我做了一次拆包。如果你確信你的變量能保證被正確初始化,那就可以這么做,否則還是不要嘗試為好。

二、Optional的作用

一個(gè)Optional對象只存在兩種狀態(tài):包含一個(gè)值,或者為空,我們都可以通過解包(unwrap)來獲取。
Swift是一種類型安全的語言。Swift通過引入Optional解決了Objective-C中“有”與“無”的問題,使代碼的安全性得到了很大的提高。
在某些場景下,Optional能起到很大的作用:

  • 當(dāng)一些屬性值可以為空時(shí),比如一個(gè)Person類中,middleName,spouse這類的屬性都可以為空
  • 當(dāng)一個(gè)方法可以返回空值,比如類型轉(zhuǎn)換函數(shù),官方文檔的例子
//try to convert a String into an Int
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber is inferred to be of type "Int?"
// 如果possibleNumber 是“hello”,則轉(zhuǎn)換不會成功,就會返回nil
  • 如果在一個(gè)字典中,使用key獲取對應(yīng)的value時(shí),返回的也應(yīng)該是Optional的值,因?yàn)槟阋部赡苷也坏絢ey對應(yīng)的value,此時(shí)返回nil。
  • 一個(gè)方法可以返回一個(gè)值,如果方法內(nèi)部產(chǎn)生了錯(cuò)誤,也可以什么都不返回。
  • Delegate 屬性(不總是需要被賦值)
  • class中weak 類型的屬性,他們所指向的值可以為空
  • 一個(gè)大的資源可以隨時(shí)被釋放,以節(jié)約空間,所以正常情況下都是空的,只有使用時(shí)才會請求賦值。
三、Optional Binding

出于類型安全的考慮,Swift不再把Optional當(dāng)作Boolean值處理。像下面這條語句在swift中會遇到編譯錯(cuò)誤

var myString: String? = "Hello"
if myString {
    print(myString)
}

但是你可以通過==和!=,將Optional值和nil做比較來判斷它是否包含一個(gè)值。如果不包含任何值,則為空。

if myString != nil{
    print("myString contain a string value of \(myString!)")
}

在上面的語句里,當(dāng)我們確定myString包含一個(gè)值時(shí),我們通過在myString后面添加一個(gè)!來進(jìn)行強(qiáng)制解包(forced unwrapping),獲取Optional內(nèi)包含的值。
但是實(shí)際上,Swift提供了一種更加方便的形式來完成這一過程,所謂的Optional Binding
看下面的代碼:

if let actualString = myString {
    print("myString contain a string value of \(actualString )")
} else {
    print("myString is nil")
}

上面代碼的意思是,如果optional string 包含一個(gè)值,我們就把這個(gè)值賦給actualString,然后就可以在if語句里繼續(xù)使用它了,所以不再需要對其進(jìn)行解包了。這樣我們就用Optional binding 代替了強(qiáng)制解包(forced unwrapping)。我們也可以使用if var actualString = myString來獲取actualString,則這個(gè)actualString就是var類型的。

四、隱式解包Optional

先看一段代碼

let possisbleString: String!

隱式解包的Optional本質(zhì)上與普通的Optional值并沒有什么不同,只是在訪問時(shí),編譯器會自動幫我們完成在變量后插入!的行為:

let possibleString: String! = "An implicity unwrapped optional string."
let implicitString: String = possibleString //此處我們不需要!來對possibleString 進(jìn)行顯示解包

很顯然,隱式解包的寫法會帶來一個(gè)潛在的危險(xiǎn),如果嘗試訪問一個(gè)為空的隱式解包Optional, 就會遇到一個(gè)runtime error。

五、Optional Chaining

Optional Chaining,如同名字一樣,我們可以通過一個(gè)鏈來安全的訪問一個(gè)Optional的屬性或者方法。
當(dāng)一個(gè)Optional值調(diào)用它的另一個(gè)Optional值的時(shí)候,Optional Chaining就形成了,基本上,Optional Chaining就是總是返回一個(gè)Optional的值,只要這個(gè)Chaining中有一個(gè)值為nil,整條Chaining就為nil,和Objective-C的向nil發(fā)消息類似。
有一點(diǎn)很有趣,就是Optional Chaining除了能將屬性返回的類型變?yōu)镺ptional外,連方法的返回值都能強(qiáng)制變?yōu)镺ptional,哪怕這個(gè)方法沒有返回值,但是別忘了,Void也算是一個(gè)類型。
可以參看兩個(gè)例子:

if let isPNG = imagePaths["star"]?.hasSuffix(".png") {
    print("The star image is in PNG format")
}
//不使用Optional Chaining需要判斷兩次
if let pet = jackon.pet {
    if let toy = pet.favoriteToy {
        toy.name
    }
}
//使用Optional Chaining只需要判斷一次
if let toy = jackon.pet?.favoriteToy {
    toy.name
}

這里通過在imagePath["star"]后面添加?來獲取star所對應(yīng)的圖片路徑,如果不存在就直接是nil,如果存在則返回對應(yīng)的path,然后緊接著調(diào)用path的hasSuffix方法。
使用Optional Chaining可以讓我們擺脫很多不必要的判斷和取值,從而精簡代碼。

六、??的使用

當(dāng)Optional解包后的值為nil時(shí),我們可以通過使用??來設(shè)置一個(gè)默認(rèn)值。
我們也可以將??鏈接起來,設(shè)置多重默認(rèn)值:
let shapePath = imagePaths["cir"] ?? imagePaths["squ"] ?? defaultImagePath
這樣每一次解包值為nil時(shí)都會設(shè)置默認(rèn)的值。

參考:
http://blog.csdn.net/zhangao0086/article/details/38640209
http://www.lxweimin.com/p/3c4e7dd6844f

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,460評論 6 538
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,067評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,467評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,468評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,184評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,582評論 1 325
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,616評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,794評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,343評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,096評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,291評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,863評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,513評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,941評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,190評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 52,026評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,253評論 2 375

推薦閱讀更多精彩內(nèi)容