[Swift4.0 官方文檔翻譯] 可選類型俊柔、異常捕獲、斷言和先決條件

可選類型

在值可能不存在的情況下纳猫,您可以使用可選類型婆咸。 一個可選類型代表兩種可能性:要么它有值,你可以通過解包來訪問該值芜辕,或者它根本沒有值尚骄。

注意:
在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ù)都應該被switchcase語句執(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)一定會終止。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末严卖,一起剝皮案震驚了整個濱河市席舍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哮笆,老刑警劉巖来颤,帶你破解...
    沈念sama閱讀 221,820評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異稠肘,居然都是意外死亡福铅,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評論 3 399
  • 文/潘曉璐 我一進店門启具,熙熙樓的掌柜王于貴愁眉苦臉地迎上來本讥,“玉大人,你說我怎么就攤上這事鲁冯】椒校” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評論 0 360
  • 文/不壞的土叔 我叫張陵薯演,是天一觀的道長撞芍。 經(jīng)常有香客問我,道長跨扮,這世上最難降的妖魔是什么序无? 我笑而不...
    開封第一講書人閱讀 59,714評論 1 297
  • 正文 為了忘掉前任验毡,我火速辦了婚禮,結(jié)果婚禮上帝嗡,老公的妹妹穿的比我還像新娘晶通。我一直安慰自己,他們只是感情好哟玷,可當我...
    茶點故事閱讀 68,724評論 6 397
  • 文/花漫 我一把揭開白布狮辽。 她就那樣靜靜地躺著,像睡著了一般巢寡。 火紅的嫁衣襯著肌膚如雪喉脖。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評論 1 310
  • 沈念sama閱讀 40,897評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼仇轻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奶甘?” 一聲冷哼從身側(cè)響起篷店,我...
    開封第一講書人閱讀 39,804評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎臭家,沒想到半個月后疲陕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡钉赁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,431評論 3 340
  • 正文 我和宋清朗相戀三年蹄殃,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片你踩。...
    茶點故事閱讀 40,561評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡诅岩,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出带膜,到底是詐尸還是另有隱情吩谦,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評論 5 350
  • 正文 年R本政府宣布膝藕,位于F島的核電站式廷,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏芭挽。R本人自食惡果不足惜滑废,卻給世界環(huán)境...
    茶點故事閱讀 41,928評論 3 334
  • 文/蒙蒙 一蝗肪、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蠕趁,春花似錦薛闪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至倔韭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瓢对,已是汗流浹背寿酌。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留硕蛹,地道東北人醇疼。 一個月前我還...
    沈念sama閱讀 48,983評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像法焰,于是被迫代替她去往敵國和親秧荆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,573評論 2 359

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

  • 關(guān)于 Swift 重要這個文檔所包含的準備信息, 是關(guān)于開發(fā)的 API 和技術(shù)的埃仪。這個信息可能會改變, 根據(jù)這個文...
    無灃閱讀 4,316評論 1 27
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理乙濒,服務發(fā)現(xiàn),斷路器卵蛉,智...
    卡卡羅2017閱讀 134,702評論 18 139
  • Swift學習有問必答群 : 313838956 ( mac版QQ有權(quán)限要求, 入群只能通過手機版 QQ申請...
    Guards翻譯組閱讀 11,614評論 3 28
  • importUIKit classViewController:UITabBarController{ enumD...
    明哥_Young閱讀 3,819評論 1 10
  • 是萬水千山 認清你來時的路 是夜 在長長的街頭獨舞 是生命的堅定信念涌向遠方 一步一步 上到天路 是不是 要的 都...
    江城妖怪閱讀 170評論 0 2