國外優秀教程精譯 | Swift 可選值詳解(上)

英文原文

Swift 是一門非常安全的語言,這裏指的是 Swift 在防止代碼意外崩潰方面做了很多努力。
代碼崩潰的一個常見原因是試圖使用一個已損壞的或不存在的數據。舉例來說,假設有一個方法:

func getHaterStatus() -> String {
return "Hate"
}

該方法沒有任何參數,並且返回一個 string:“Hate”。然而有一天陽光明媚,這些心有怨恨的傢伙們不感到怨恨了——會發生什麼事?我們可能就不想返回任何東西了:畢竟這些人今天不怨恨了嘛。
這時,一個空字符串也許是最好用來表明“沒啥事”的,有時這也是事實。但是用數字如何表現“什麼都沒有”—— 用 0 表示“空數字”還是 -1 ?
別急着定義你自己的表示規則,Swift 已經有了解決方案:可選值。可選值就是可能有值也可能沒有值的變量。大多數人都覺得可選值很難理解,別擔心——我將用各種方式來說明它,總有一種方式適合你。
當使用 -> String 時,表示“這個方法會返回一個確定的 string ”,即表示該方法不會返回空值,因此它被稱爲安全的,因爲你總是能獲得一個值,這個值類型是 string 。反之,如果方法可能返回有值或空值,需要改寫爲下面這樣:

func getHaterStatus() -> String? {
    return "Hate"
}

注意這個額外的問號 String? 代表“可選的 string ”。當前的代碼無論如何都會返回 “Hate”,讓我們來修改一下:如果天氣晴朗,懷恨者們將展開新的生活篇章,放棄他們仇恨的生活,因此返回空值。在 Swift 中,空值有特別的關鍵字: nil
於是代碼改爲這樣:

func getHaterStatus(weather: String) -> String? {
    if weather == "sunny" {
        return nil
    } else {
        return "Hate"
    }
}

方法接受一個 string 參數(天氣 weather )並返回一個 string (仇恨狀態),但返回值可能有也可能沒有——爲 nil 。具體到本文例子中,就表示我們可能得到一個 string ,或者得到 nil 。
那麼重點來了:Swift 希望你的代碼是真正安全的,使用 nil 就不是一個好主意。它會使你的代碼崩潰,混淆你的代碼邏輯,或者在界面上顯示錯誤的內容。因此,當聲明一個值爲可選值之時,Swift 就要確保你能安全地處理它。
試着把下面這行添加到你的 playground :

var status: String
status = getHaterStatus(weather: "rainy")

第一行創建一個 string 型變量,第二行把 getHaterStatus()的返回值賦值給這個變量——參數表示今天下雨,這樣可以保證仇恨者們正在仇恨中(返回值是"Hate”)。
這段代碼無法運行,因爲我們聲明的 String 型變量 status 要求一個值,但getHaterStatus()可能無法提供,因爲它返回可選值。也就是說,我們表示 status 一定是個 string ,但實際上 getHaterStatus()可能啥也不返回。Swift 根本不會讓你犯這個錯誤,這非常有用,因爲它有效地阻止了一大堆常見錯誤。
爲了解決這個問題,我們需要把 status聲明爲 String?,或者乾脆移除類型聲明,讓 Swift 自己推斷類型。第一種方法如下:

var status: String?
status = getHaterStatus(weather: "rainy")

第二種方式:

var status = getHaterStatus(weather: "rainy")

無論選擇哪個,值都可能爲空值,Swift 是不會讓你冒險使用的。舉例來說,假設有這麼一個方法:

func takeHaterAction(status: String) {
    if status == "Hate" {
        print("Hating")
    }
}

這段代碼接受一個 string ,根據 string 內容打印一條消息。這個方法接受一個 String 值,卻不是 String?——你無法傳遞一個可選值給它,它需要一個確切的值,導致無法把 status 變量傳給它。
Swift 有兩個解決辦法。兩個都能用,但只能二選一。第一個解決方案是解包,它在條件語句中使用特定的語法規則。解包一次性做兩件事情:檢查可選值是否有值,如果有值就將其放入一個非可選值中並運行對應代碼塊。
解包語法如下:

if let unwrappedStatus = status {
    // unwrappedStatus 是非可選值
} else {
    // 解包失敗的處理
}

if let在一行代碼中簡潔地處理了檢查和解包,這種方法非常常見。應用這種方法,我們能安全地把 getHaterStatus() 解包,並僅在非可選 string 有效存在時,才調用 takeHaterAction()
這是完整代碼:

func getHaterStatus(weather: String) -> String? {
    if weather == "sunny" {
        return nil
    } else {
        return "Hate"
    }
}

func takeHaterAction(status: String) {
    if status == "Hate" {
        print("Hating")
    }
}

if let haterStatus = getHaterStatus(weather: "rainy") {
    takeHaterAction(status: haterStatus)
}

未完待續

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章