
Swift 是一門非常安全的語言,這里指的是 Swift 在防止代碼意外崩潰方面做了很多努力。
代碼崩潰的一個常見原因是試圖使用一個已損壞的或不存在的數(shù)據(jù)。舉例來說,假設有一個方法:
func getHaterStatus() -> String {
return "Hate"
}
該方法沒有任何參數(shù),并且返回一個 string:“Hate”。然而有一天陽光明媚,這些心有怨恨的家伙們不感到怨恨了——會發(fā)生什么事?我們可能就不想返回任何東西了:畢竟這些人今天不怨恨了嘛。
這時,一個空字符串也許是最好用來表明“沒啥事”的,有時這也是事實。但是用數(shù)字如何表現(xiàn)“什么都沒有”—— 用 0 表示“空數(shù)字”還是 -1 ?
別急著定義你自己的表示規(guī)則,Swift 已經(jīng)有了解決方案:可選值??蛇x值就是可能有值也可能沒有值的變量。大多數(shù)人都覺得可選值很難理解,別擔心——我將用各種方式來說明它,總有一種方式適合你。
當使用 -> 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 參數(shù)(天氣 weather )并返回一個 string (仇恨狀態(tài)),但返回值可能有也可能沒有——為 nil 。具體到本文例子中,就表示我們可能得到一個 string ,或者得到 nil 。
那么重點來了:Swift 希望你的代碼是真正安全的,使用 nil 就不是一個好主意。它會使你的代碼崩潰,混淆你的代碼邏輯,或者在界面上顯示錯誤的內容。因此,當聲明一個值為可選值之時,Swift 就要確保你能安全地處理它。
試著把下面這行添加到你的 playground :
var status: String
status = getHaterStatus(weather: "rainy")
第一行創(chuàng)建一個 string 型變量,第二行把 getHaterStatus()的返回值賦值給這個變量——參數(shù)表示今天下雨,這樣可以保證仇恨者們正在仇恨中(返回值是"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 ,根據(jù) string 內容打印一條消息。這個方法接受一個 String 值,卻不是 String?——你無法傳遞一個可選值給它,它需要一個確切的值,導致無法把 status 變量傳給它。
Swift 有兩個解決辦法。兩個都能用,但只能二選一。第一個解決方案是解包,它在條件語句中使用特定的語法規(guī)則。解包一次性做兩件事情:檢查可選值是否有值,如果有值就將其放入一個非可選值中并運行對應代碼塊。
解包語法如下:
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)
}