國外優(yōu)秀教程精譯 | Swift 可選值詳解(下)

如果看完 Swift 可選值詳解(上)后,你對(duì)可選值還是有些迷惑,甚至一頭霧水,那么我們來換一種方式來解釋。
看下面的方法:

func yearAlbumReleased(name: String) -> Int {
    if name == "Taylor Swift" { return 2006 }
    if name == "Fearless" { return 2008 }
    if name == "Speak Now" { return 2010 }
    if name == "Red" { return 2012 }
    if name == "1989" { return 2014 }

    return 0
}

該方法要求傳入一個(gè) Taylor Swift 專輯的名稱,返回對(duì)應(yīng)的發(fā)行年份。 但是如果我們傳入了專輯名稱“Lantern”,因?yàn)槲覀儗?Taylor Swift 與 Hudson Mohawke 混為一談(這是一個(gè)容易犯的錯(cuò)誤,對(duì)吧?),該方法就返回 0,因?yàn)樗皇?Taylor 的專輯之一。
但 0 在這里有意義嗎? 當(dāng)然,如果專輯是在公元 0 年發(fā)布的,當(dāng)時(shí)凱撒奧古斯是羅馬的皇帝,0 可能有意義,但在這里只會(huì)讓人感到困惑 —— 人們需要提前知道 0 的含義是“不被認(rèn)可”。(譯者注:這里說明的是,0 代表的含義需要提前定義,否者很容易被當(dāng)作年份處理)
最好是改寫為返回 int (有對(duì)應(yīng)年份時(shí))或 nil (沒有對(duì)應(yīng)年份),可選值使這一切變得容易。下面是改寫后的方法:

func yearAlbumReleased(name: String) -> Int? {
    if name == "Taylor Swift" { return 2006 }
    if name == "Fearless" { return 2008 }
    if name == "Speak Now" { return 2010 }
    if name == "Red" { return 2012 }
    if name == "1989" { return 2014 }

    return nil
}

由于用了可選值,我們需要用 if let 解包,因?yàn)樾枰獧z查值是否存在。
再來看另一種情形:

var items = ["James", "John", "Sally"]

現(xiàn)在我們想輸出其中一個(gè)名字對(duì)應(yīng)的索引值,我們可能這樣寫:

func position(of string: String, in array: [String]) -> Int {
    for i in 0 ..< array.count {
        if array[i] == string {
            return i
        }
    }

    return 0
}

其中循環(huán)檢查了數(shù)組成員,返回了對(duì)應(yīng)名稱的索引值,如果沒找到,返回 0 。
現(xiàn)在試著運(yùn)行下面 4 行代碼:

let jamesPosition = position(of: "James", in: items)
let johnPosition = position(of: "John", in: items)
let sallyPosition = position(of: "Sally", in: items)
let bobPosition = position(of: "Bob", in: items)

將輸出 0, 1, 2, 0 —— James 和 Bob 的位置是一樣的,然而他們實(shí)際上一個(gè)存在,而另一個(gè)并不存在。這是因?yàn)槲矣?0 表示 “不存在” 造成的。圖省事的話,可以用 -1 來表示不存在,但這樣一來,又必須時(shí)刻記得有這個(gè)特殊值意味著“不存在”。(譯者注:-1 引發(fā)的崩潰相信大家都有經(jīng)歷過,在 C 語言中,有符號(hào) -1 轉(zhuǎn)換為無符號(hào)時(shí),為無符號(hào)的最大值,從而導(dǎo)致很多混亂)
解決辦法還是可選值:用 nil 表示沒有找到對(duì)應(yīng)名稱。這也是數(shù)組內(nèi)置查找方法 someArray.firstIndex(of: someValue)的實(shí)際做法。
當(dāng)你要對(duì)付“可能有可能沒有”的值,Swfit 強(qiáng)制要求在使用前解包,以此明確這里可能沒有值。if let就是用來做這個(gè)的:如果有值就解包后使用,否者完全不使用它。Swift 根本不讓你憑空地使用一個(gè)可能為空的值。

強(qiáng)制解包

Swift 允許使用感嘆號(hào) !來屏蔽它的安全性。當(dāng)你能確認(rèn)可選值有值時(shí),可以將感嘆號(hào)放在這個(gè)值后面來強(qiáng)制解包它。

請(qǐng)小心:如果你強(qiáng)制解包一個(gè)沒有值的可選值時(shí),會(huì)引發(fā)代碼崩潰。

下面是一些協(xié)同工作的代碼:

func yearAlbumReleased(name: String) -> Int? {
    if name == "Taylor Swift" { return 2006 }
    if name == "Fearless" { return 2008 }
    if name == "Speak Now" { return 2010 }
    if name == "Red" { return 2012 }
    if name == "1989" { return 2014 }

    return nil
}

var year = yearAlbumReleased(name: "Red")

if year == nil {
    print("There was an error")
} else {
    print("It was released in \(year)")
}

代碼獲得了專輯的發(fā)行年份。如果沒找到專輯,year被置空,將打印出錯(cuò)信息。否則正確的發(fā)行年份會(huì)被打印。
是嗎?好吧,因?yàn)?yearAlbumReleased()返回的是可選值,這里由沒有使用 if let來解包。結(jié)果,實(shí)際輸出的是“It was released in Optional(2012)”——可能并不是我們想要的。
關(guān)鍵是,我們已經(jīng)檢查了可選值的有效性,再用 if let進(jìn)行所謂的安全解包顯得有點(diǎn)無意義。因此,Swift 提供了解決辦法——把上面代碼中第二個(gè)print() 改為這樣:

print("It was released in \(year!)")

注意這個(gè)感嘆號(hào):它的意思是“我保證這里有值,強(qiáng)制解包吧”。

隱形解包

你還能用感嘆號(hào)來創(chuàng)建隱形解包可選值,這是很多人真正感到困惑的地方。那么就好好讀讀下面的介紹。

  • 靜態(tài)變量必須含有值。比如:String 必須含有 string ,哪怕是空字符串"",不能為 nil 。
  • 可選值可以有值也可以沒有。使用前必須解包。比如 String? 可能為 string ,或者是 nil ,確定的辦法就是解包。
  • 隱形解包可選值可以有值也可以沒有。但使用前不需要解包。Swift 不再為你檢查,所以你使用時(shí)要額外小心。比如 String! 可能為 string ,或者是 nil ——取決于你適當(dāng)?shù)氖褂盟?。它類似靜態(tài)變量,Swift 允許你直接訪問它的值,不需要安全地解包。如果你要這么做,意味著你知道一定有值,否者程序會(huì)崩。

使用隱形解包可選值的主要情形是在使用 UIKit 的界面元素時(shí)(macOS 里對(duì)應(yīng) AppKit ),這些需要事先聲明,但是在創(chuàng)建之前你不能使用它們 —— 而 Apple 喜歡在最后一刻創(chuàng)建用戶界面元素以避免任何不必要的工作。 必須不斷地打開你肯定知道的值,這會(huì)使人厭煩,所以這些是隱式解開的。
不要擔(dān)心,如果您發(fā)現(xiàn)隱式解包的選項(xiàng)有點(diǎn)難以理解 - 當(dāng)您使用該語言時(shí),它將變得清晰。

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

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