可選類型
在值可能不存在的情況下纳猫,您可以使用可選類型婆咸。 一個可選類型代表兩種可能性:要么它有值,你可以通過解包來訪問該值芜辕,或者它根本沒有值尚骄。
注意:
在C或Objective-C中不存在可選類型的概念。 在OC中跟可選類型這
個概念最類似的是一個要么返回對象要么返回nil的方法侵续,其中nil表
示“對象不存在”倔丈。然而這種方法僅適用于對象,它并不適用于結(jié)構(gòu)
體状蜗、基本類型或枚舉需五。 對于這些類型,OC方法通常返回一個特殊值
(例如NSNotFound)來表示沒有值轧坎。 這種機制假設了方法調(diào)用者知
道存在一個特殊的值宏邮,而且會通過這個值來檢測方法的結(jié)果。 Swift
的可選類型則不需要特殊常量缸血, 就可讓您標明任何類型的值都不存在蜜氨。
這里有一個例子說明了如何使用可選類型來處理不存在的值。 Swift的Int類型有一個初始化方法捎泻,它會嘗試將一個String類型的值轉(zhuǎn)換成一個Int類型飒炎。 但是,并不是每個字符串都可以轉(zhuǎn)換為Int類型笆豁。 字符串“123”可以轉(zhuǎn)換為數(shù)值123郎汪,但字符串“hello,world”明顯沒有數(shù)字可轉(zhuǎn)換為Int闯狱。
以下的示例使用這個初始化方法嘗試將一個String轉(zhuǎn)換為Int:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber被轉(zhuǎn)換為類型"Int?"煞赢,即“可選類型Int”
因為初始化方法可能會失敗,所以它返回的是一個可選類型Int扩氢,而不是Int耕驰。 一個可選類型Int我們用Int?表示,而不是Int。 問號表示這個Int它包含的值是可選的朦肘,這意味著它可能包含一些具體的Int值饭弓,或者它可能不包含任何值。 (它不能包含任何其他值媒抠,例如Bool值或String值弟断,它要么就是一個Int,要么它根本就沒有值)
Nil
通過賦值nil趴生,你可以將一個可選類型設置為無值狀態(tài):
var serverResponseCode:Int阀趴? = 404
// serverResponseCode包含一個具體的Int值,即404
serverResponseCode = nil
// serverResponseCode現(xiàn)在不包含任何值
注意
nil不能和非可選類型的常量和變量一起使用苍匆。 如果您的代碼中的常
量或變量在某些情況下需要以沒有值的狀態(tài)運行刘急,那您應該始終將其
聲明為可選類型。
如果您定義了一個可選類型的變量浸踩, 但是不設置默認值叔汁,那么該變量的值將自動設置為nil:
var surveyAnswer:String?
// surveyAnswer自動設置為nil
注意
Swift中的nil不同于OC中的nil检碗。 在OC中据块,nil是指向不存在的對象的
指針。 在Swift中折剃,nil不是指針——它是一個特定類型不存在值的一
種表現(xiàn)形式另假。 任何數(shù)據(jù)類型的可選類型都可以設置為nil,而不僅僅
是對象類型怕犁。
If和強制解包
您可以通過使用if語句比較可選類型和nil边篮,進而確定這個可選類型是否包含具體的值。您可以使用“等于”運算符(==)或“不等于”運算符(奏甫!=)執(zhí)行此比較苟耻。
如果一個可選類型有一個值,即它被認為是“不等于”nil:
if convertedNumber扶檐!= nil {
print(“convertedNumber包含一些整數(shù)值”)
}
//打印結(jié)果:“convertedNumber包含一些整數(shù)值”。
一旦您確定可選類型確實有值胁艰,您可以通過在可選類型名稱的末尾添加感嘆號(!)來訪問其包含的值款筑。這個感嘆號如同在宣稱:“我知道這個可選類型肯定有值,請使用它腾么∧问幔” 這被稱為可選類型的強制解包:
if convertedNumber!= nil {
print(“convertedNumber的值為\(convertedNumber!)”)
}
// 打印結(jié)果: “convertedNumber的值為123”
關(guān)于if語句的更多信息解虱,請參閱控制流(Control Flow)攘须。
注意
試圖使用!訪問不存在值的可選類型會觸發(fā)運行錯誤。在使用!強制解包
前要始終確迸固可選類型包含非空的值于宙。
可選綁定
您可以使用可選綁定來確定可選類型是否包含值浮驳,如果這樣做,你必須把這個綁定的值用作臨時常量或變量捞魁。
可選綁定可以與if和while語句一起使用至会,以檢查可選類型中的值,并作為這個操作的一部分將該值提取為常量或變量谱俭。 if和while語句在控制流(Control Flow)中有更詳細的描述奉件。
為if語句編寫可選綁定如下所示:
if let constantName = someOptional {
//statements
}
您可以使用可選綁定重寫“可選類型”部分的possibleNumber示例,而不是使用強制解包:
if let actualNumber = Int(possibleNumber){
print("\"\(possibleNumber)\"的整數(shù)值為\(actualNumber)")
} else {
print("\"\(possibleNumber)\"無法轉(zhuǎn)換為Int類型")
}
// 打印結(jié)果: ”123“的整數(shù)值為123“
這段代碼可以這樣理解:
“如果Int(possibleNumber)方法返回的可選Int包含一個值昆著,創(chuàng)建一個名為actualNumber的新常量并設置其值為可選類型包含的值县貌。”
如果轉(zhuǎn)換成功凑懂,則常量actualNumber可用于if語句的第一個分支中煤痕。它已經(jīng)通過可選類型中的值初始化了,所以沒有必要使用征候!后綴訪問其值杭攻。在這個例子中,actualNumber只簡單地用于打印轉(zhuǎn)換后的結(jié)果疤坝。
您可以同時將常量和變量與可選綁定一起使用兆解。如果要在if語句的第一個分支中操作actualNumber的值,您可以編寫“if var actualNumber”來替代原來的寫法跑揉,并且可選類型中包含的值將作為變量而不是常量使用锅睛。
您可以在單個if語句中按你的需求包含多個可選綁定和Boolean條件,并以逗號分隔历谍。如果可選綁定中的任何值為nil或任何Boolean條件為false现拒,則整個if語句的條件被認為是false。以下第二段if代碼和第一段是等效的:
if let firstNumber = Int("4"), let secondNumber = Int("42"),
firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
// 打印結(jié)果: "4 < 42 < 100"
if let firstNumber = Int("4") {
if let secondNumber = Int("42") {
if firstNumber < secondNumber && secondNumber < 100 {
print("\(firstNumber) < \(secondNumber) < 100")
}
}
}
// 打印結(jié)果: "4 < 42 < 100”
注意
在if語句中使用可選綁定創(chuàng)建的常量和變量僅在if語句的主體內(nèi)可用望侈。
相比之下印蔬,使用guard語句創(chuàng)建的常量和變量可以在guard語句后面
的代碼中使用,如(Early Exit章節(jié))所述脱衙。
隱式解包可選類型
如上所述侥猬,使用可選類型意味著允許常量或變量可以“無值”。使用if語句可以查看可選類型的值是否存在捐韩,并且可以使用可選綁定解包以訪問可選類型的值(如果存在)退唠。
在設置了初始值后,有時從程序的結(jié)構(gòu)中可以清楚地看到荤胁,可選類型將始終有值瞧预。在這種情況下,每次訪問可選類型的值時沒有必要進行檢查和解包,因為我們可以安全地假定任何情況下它都是有值的垢油。
這種可選類型被定義為隱式解包可選類型盆驹。通過在想要設置為可選類型的參數(shù)后添加一個感嘆號(String!),而不是一個問號(String?)秸苗,你就可以得到一個隱式解包的可選類型召娜。
當可選類型在初始化后確認其值肯定存在,并且可以肯定地假設存在于之后的任何時刻惊楼,那么隱式解包可選類型將提供很大的幫助玖瘸。在Swift中使用隱式解包可選類型主要是在初始化類的時候,如不持有屬性和隱式解包可選類型特性(Unowned References and Implicitly Unwrapped Optional Properties)章節(jié)所述檀咙。
隱式解包可選類型是一個常規(guī)選擇雅倒,但也可以像非可選類型一樣使用,而無需在每次訪問時進行解包弧可。 以下示例展示了當訪問可選String類型與隱式解包可選String類型的一個具體String值時蔑匣, 兩者的差異:
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // 要求加上!來解包
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // 不需要加上!也可以解包
您可以將隱式解包可選類型視為給予一個可選類型權(quán)利,在任何它被使用的時候它都可以自動解包棕诵。 比起在每次使用可選類型時加上一個感嘆號時裁良,您只需要在聲明它時在其后添加一個感嘆號。
注意
如果一個隱式解包可選類型的值為nil校套,當您嘗試訪問其值時則會觸發(fā)
運行時錯誤价脾。 其原因與給一個值為空的常規(guī)可選類型添加感嘆號的情形完全相同。
您仍然可以將隱式解包可選類型視為常規(guī)的可選類型笛匙,以檢查它是否包含一個值:
if assumedString != nil {
print(assumedString)
}
// 打印結(jié)果: "An implicitly unwrapped optional string.”
您還可以使用隱式解包可選類型與可選綁定侨把,在單個語句中檢查和解包其值:
if let definiteString = assumedString {
print(definiteString)
}
// 打印結(jié)果: "An implicitly unwrapped optional string.”
注意
當一個變量在以后有可能為nil時,不要使用隱式解包可選類型妹孙。在變
量的生命周期內(nèi)如果需要檢查它是否為nil秋柄,請始終使用常規(guī)的可選類型。
錯誤處理
您可以使用錯誤處理來應對您的程序在執(zhí)行過程中可能遇到的錯誤蠢正。
與可選類型相反骇笔,錯誤處理可以使用值的存在或不存在來傳達一個功能的成功或失敗,允許您確定故障的根本原因嚣崭,如有必要蜘拉,它還能將錯誤傳送到程序的另一部分。
當函數(shù)遇到錯誤條件時有鹿,會引發(fā)錯誤并拋出異常。 那個函數(shù)的調(diào)用者可以捕獲錯誤并作出適當?shù)捻憫?/p>
func canThrowAnError() throws {
//這個函數(shù)可能會拋出一個錯誤
}
一個函數(shù)通過在其聲明中包含throws關(guān)鍵字來說明它可以拋出異常谎脯。 當你調(diào)用一個可以拋出異常的函數(shù)時葱跋,你可以在表達式中添加try關(guān)鍵字。
Swift會自動將錯誤傳送到當前區(qū)間之外,直到被catch語句處理娱俺。
do {
try canThrowAnError()
// no error was thrown
} catch {
// an error was thrown
}
do語句創(chuàng)建一個新的包含范圍稍味,它允許將錯誤傳播到一個或多個catch子句。
以下是一個示例荠卷,說明如何使用錯誤處理來響應不同的錯誤條件:
func makeASandwich() throws {
// ...
}
do {
try makeASandwich()
eatASandwich()
} catch SandwichError.outOfCleanDishes {
washDishes()
} catch SandwichError.missingIngredients(let ingredients) {
buyGroceries(ingredients)
}
在此示例中模庐,如果沒有可用的干凈碗筷或缺少任何食材,那么makeASandwich()方法就會拋出錯誤油宜。 因為makeASandwich()可以引發(fā)錯誤掂碱,而且方法的調(diào)用被限定在一個try表達式中。 通過在do語句中調(diào)用方法,拋出的任何錯誤將被傳送到catch語句進行處理。
如果沒有錯誤發(fā)生标锄,那么eatASandwich()方法就會被調(diào)用癌别。 如果拋出一個錯誤并且匹配到SandwichError.outOfCleanDishes,那么washDishes()函數(shù)將會被調(diào)用峻黍。 如果拋出的錯誤匹配到SandwichError.missingIngredients,則會調(diào)用buyGroceries(_ :)方法,并把catch捕獲的關(guān)聯(lián)值(String類型)傳給它撬即。
錯誤處理的章節(jié)對拋出,捕獲和傳送錯誤進行了更詳細的描述呈队。
斷言(Assertions)和先決條件(Preconditions)
斷言和先決條件是會在運行時進行的檢查手段剥槐。在執(zhí)行任何其他代碼之前,您可以使用它們來確钡嘀洌基本條件已經(jīng)滿足才沧。如果斷言或前提條件中的布爾條件為true,則代碼照常執(zhí)行绍刮。如果條件為false温圆,那么程序當前處于無效狀態(tài);代碼會結(jié)束執(zhí)行孩革,您的應用程序也會被終止岁歉。
寫代碼時,使用斷言和先決條件您可以表達出你需要的期望條件膝蜈,因此您可以將它們作為代碼的一部分锅移。斷言可幫助您在開發(fā)過程中發(fā)現(xiàn)錯誤和錯誤假設,先決條件可幫助您檢測發(fā)布后的問題饱搏。除了在運行時驗證您的期望條件外非剃,斷言和先決條件也是代碼中一種有效的文檔形式。與先前在錯誤處理中討論的錯誤條件不同推沸,斷言和先決條件不用于可恢復的或期望的錯誤备绽,因為失敗的斷言或先決條件表示無效的程序狀態(tài)券坞,所以無法捕獲失敗的斷言。
使用斷言和先決條件并不意味著在您的代碼中無效條件就一定不會出現(xiàn)肺素。 然而恨锚,使用它們來強制執(zhí)行有效的數(shù)據(jù),會使您的應用程序在錯誤發(fā)生并被強制終止時倍靡,更具備可監(jiān)測性猴伶,而且有助于調(diào)試。 一旦檢測到無效狀態(tài)塌西,停止執(zhí)行也有助于將其引起的損壞最小化他挎。
斷言和先決條件之間的區(qū)別在于被執(zhí)行的時間點有所不同:斷言僅在調(diào)試版本中執(zhí)行,但在調(diào)試和發(fā)布版本中先決條件都會被執(zhí)行雨让。 在發(fā)布版本中雇盖,斷言內(nèi)的條件不再被執(zhí)行。 這意味著您可以在開發(fā)過程中使用盡可能多地使用斷言栖忠,因為這并不會影響發(fā)布版本的性能崔挖。
用斷言進行調(diào)試
通過從Swift標準庫調(diào)用assert(::file:line:)方法,您寫下一個斷言庵寞。 您將一段文字和一個結(jié)果為true或false的表達式賦值給這個方法狸相,如果表達式的結(jié)果為false,則顯示這段文字捐川。 例如:
let age = -3
assert(age >= 0, "A person's age can't be less than zero.")
// This assertion fails because -3 is not >= 0.
在這個例子中脓鹃,如果age> = 0的值為true,那么代碼繼續(xù)執(zhí)行古沥,即age為正值瘸右。 如果age為負值,如上面的代碼所示岩齿,age> = 0為false太颤,斷言失敗,終止應用程序盹沈。
你可以省略斷言發(fā)布的文字——例如當它只是一個單調(diào)的重復條件龄章。
assert(age >= 0)
如果代碼已經(jīng)檢查了條件,則使用assertionFailure(_:file:line:)方法來標明斷言失敗了乞封。 例如:
if age > 10 {
print("You can ride the roller-coaster or the ferris wheel.")
} else if age > 0 {
print("You can ride the ferris wheel.")
} else {
assertionFailure("A person's age can't be less than zero.")
}
執(zhí)行先決條件
當條件有可能為false的時候使用先決條件做裙,但是必須確保您的代碼會繼續(xù)執(zhí)行。 例如肃晚,您可以使用先決條件來檢查數(shù)組是否越界锚贱,或者檢查方法是否接收到一個有效的值。
通過調(diào)用precondition(::file:line:)方法您可以編寫先決條件关串。 您需要給此方法賦值一個表達式拧廊,其計算結(jié)果為true或false杂穷,如果條件的結(jié)果為false,則顯示一段文字(也是由你賦值)卦绣。 例如:
// In the implementation of a subscript...
precondition(index > 0, "Index must be greater than zero.")
您還可以調(diào)用preconditionFailure(_:file:line:)方法來標明錯誤的出現(xiàn)——例如,如果所有輸入的有效數(shù)據(jù)都應該被switch的case語句執(zhí)行飞蚓, 但程序執(zhí)行了switch結(jié)構(gòu)中的default語句滤港。
注意
如果在**-Ounchecked**模式下進行編譯,則不執(zhí)行先決條件趴拧。 編譯
器假設先決條件始終為真溅漾,并優(yōu)化您的相應的代碼。 但是無論如何
設置著榴,**fatalError(_:file:line:)**方法總是不執(zhí)行添履,。
在早期開發(fā)中你可以使用**fatalError(_:file:line:) **方法給你還未實現(xiàn)
的功能創(chuàng)建存根脑又,例如暮胧,使用**fatalError("Unimplemented")**方法作
為方法的實現(xiàn)。 與斷言或前提條件不同问麸,因為致命錯誤從未被優(yōu)
化往衷,您可以確保程序在執(zhí)行如果遇到存根實現(xiàn)一定會終止。