從 Objective-C 轉(zhuǎn) Swift 開發(fā)已經(jīng)有一段時(shí)間了,這兩門語言在整體的理念上差異還是蠻大的峰伙。在這之中,可選類型的處理是每一個(gè)使用 Swift 的開發(fā)者每天都要面臨的問題音同,理解并正確處理好可選類型對(duì)于寫出高質(zhì)量的 Swift 代碼和保證 iOS 項(xiàng)目的健壯性都是至關(guān)重要的词爬。
可選類型
要想處理好可選類型,就要先理解可選類型权均。
一個(gè)可選類型代表有兩種可能性:有一個(gè)值顿膨,你可以解包可選類型來訪問該值锅锨;或者根本沒有值。
在 Objective-C 中不存在可選類型的概念恋沃,Objective-C 中最接近的東西就是 nil, nil 的意思是“沒有有效的對(duì)象”必搞。但是,這只適用于對(duì)象——它不適用于結(jié)構(gòu)囊咏、基本數(shù)據(jù)類型或枚舉值恕洲。對(duì)于這些類型,Objective-C 方法通常會(huì)返回一個(gè)特殊值(如 NSNotFound)來指示缺少值梅割。這種方法假設(shè)方法的調(diào)用者知道有一個(gè)特殊的值來測(cè)試霜第,并記得檢查它。Swift 的可選值可以讓你指出可能為 nil 的任何類型的值户辞,而不需要特殊的常量泌类。
例如,Swift 的 Int 類型有一個(gè)初始化方法底燎,它試圖將一個(gè) String 值轉(zhuǎn)換成一個(gè) Int 值刃榨。但是,并不是每個(gè)字符串都可以轉(zhuǎn)換成一個(gè)整數(shù)双仍。字符串 "123" 可以轉(zhuǎn)換為數(shù)字值 123枢希,但字符串 "Hello, world" 沒有一個(gè)明顯的數(shù)值要轉(zhuǎn)換。
下面的例子使用初始化方法來嘗試將一個(gè)字符串轉(zhuǎn)換為一個(gè) Int:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推斷為 "Int?" 類型或 “可選的 Int”
nil
通過賦值給它一個(gè)特殊的值 nil 來設(shè)置一個(gè)可選變量為無值狀態(tài):
var serverResponseCode: Int? = 404
// serverResponseCode 包含一個(gè)實(shí)際的 Int 值為 404
serverResponseCode = nil
// serverResponseCode 現(xiàn)在不包含任何值
如果你定義了一個(gè)可選變量而不提供默認(rèn)值朱沃,則該變量會(huì)自動(dòng)設(shè)置為 nil:
var surveyAnswer: String?
// surveyAnswer 自動(dòng)設(shè)置為 nil
Swift 的 nil 與 Objective-C 中的 nil 不相同苞轿。在 Objective-C 中,nil 是一個(gè)指向不存在對(duì)象的指針逗物。在 Swift 中呕屎,nil 不是一個(gè)指針,它是缺少某種類型的值敬察。任何類型的可選值都可以被設(shè)置為 nil秀睛,而不僅僅是對(duì)象類型。
可選綁定
你可以使用可選綁定來發(fā)現(xiàn)可選值是否包含值莲祸,如果有蹂安,則使用該值用作臨時(shí)常量或變量∪裰模可選綁定可以與 if 和 while 語句一起使用田盈,以檢查可選值內(nèi)部的值,并將該值提取為常量或變量缴阎,作為單次操作的一部分允瞧。
使用 if 語句編寫一個(gè)可選綁定,如下所示:
if let actualNumber = Int(possibleNumber) {
print("\"\(possibleNumber)\" has an integer value of \(actualNumber)")
} else {
print("\"\(possibleNumber)\" could not be converted to an integer")
}
// 打印 ""123" has an integer value of 123"
如果轉(zhuǎn)換成功,那么 actualNumber 常量可以在 if 語句的第一個(gè)分支中使用述暂。它已經(jīng)被初始化為包含在非可選的值中痹升,所以沒有必要使用 ! 后綴來訪問它的值。
你可以使用可選綁定的常量和變量畦韭。如果你想在 if 語句的第一個(gè)分支內(nèi)操作 actualNumber 的值疼蛾,你可以寫 if var actualNumber,使得可選值作為一個(gè)變量而非常量艺配。
你可以根據(jù)需要在單個(gè) if 語句中包含盡可能多的可選綁定和布爾條件察郁,并用逗號(hào)分隔。如果可選綁定中的任何值為 nil转唉,或者任何布爾條件的計(jì)算結(jié)果為 false皮钠,則整個(gè) if 語句的條件被認(rèn)為是錯(cuò)誤的。以下 if 語句是等價(jià)的:
if let firstNumber = Int("4"), let secondNumber = Int("42"), firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// 打印 "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// 打印 "4 < 42 < 100"
隱式解包可選類型
有時(shí)從程序的結(jié)構(gòu)中可以清楚的看到赠法,在第一次設(shè)置值之后鳞芙,可選值將始終有一個(gè)值。在這些情況下期虾,每次訪問時(shí)都不需要檢查和解包可選值,因?yàn)榭梢园踩丶俣ㄋ械臅r(shí)間都有一個(gè)值驯嘱。
這些可選值被定義為隱式解包可選值镶苞。你寫一個(gè)隱式解包的可選值,在你想要的可選類型之后放置一個(gè)感嘆號(hào)(String!)而不是一個(gè)問號(hào)(String?)
隱式解包可選值的背后是普通可選值鞠评,但也可以像非可選值一樣使用茂蚓,而不必在每次訪問時(shí)解包可選值。
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 需要感嘆號(hào)
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要感嘆號(hào)
如果隱式解包可選值為 nil剃幌,并且你嘗試訪問其包裝的值聋涨,則會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤。
你仍然可以對(duì)隱式解包可選值使用強(qiáng)制解包和可選綁定负乡。