1.表示并拋出錯誤
在 Swift 中,錯誤用符合Error協(xié)議的類型的值來表示。這個空協(xié)議表明該類型可以用于錯誤處理。
Swift 的枚舉類型尤為適合構(gòu)建一組相關(guān)的錯誤狀態(tài),枚舉的關(guān)聯(lián)值還可以提供錯誤狀態(tài)的額外信息。
enum VendingMachineError : Error {
case invalidSelection //選擇無效
case insufficientFunds(coinsNeeded: Int) //金額不足
case outOfStock //缺貨
}
2.處理錯誤
某個錯誤被拋出時,附近的某部分代碼必須負(fù)責(zé)處理這個錯誤,例如糾正這個問題、嘗試另外一種方式、或是向用戶報(bào)告錯誤。
Swift 中有4種處理錯誤的方式。你可以把函數(shù)拋出的錯誤傳遞給調(diào)用此函數(shù)的代碼、用do-catch語句處理錯誤、將錯誤作為可選類型處理、或者斷言此錯誤根本不會發(fā)生。
注意
Swift 中的錯誤處理和其他語言中用try,catch和throw進(jìn)行異常處理很像。和其他語言中(包括 Objective-C )的異常處理不同的是,Swift 中的錯誤處理并不涉及解除調(diào)用棧,這是一個計(jì)算代價(jià)高昂的過程。就此而言,throw語句的性能特性是可以和return語句相媲美的。
- 用 throwing 函數(shù)傳遞錯誤
為了表示一個函數(shù)、方法或構(gòu)造器可以拋出錯誤,在函數(shù)聲明的參數(shù)列表之后加上throws關(guān)鍵字。一個標(biāo)有throws關(guān)鍵字的函數(shù)被稱作throwing函數(shù)。如果這個函數(shù)指明了返回值類型,throws關(guān)鍵詞需要寫在箭頭(->)的前面。
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
注意
只有 throwing 函數(shù)可以傳遞錯誤。任何在某個非 throwing 函數(shù)內(nèi)部拋出的錯誤只能在函數(shù)內(nèi)部處理。”
struct Item {
var price: Int
var count: Int
}
class VendingMachine {
var inventory = [
"Candy Bar": Item(price: 12, count: 7),
"Chips": Item(price: 10, count: 4),
"Pretzels": Item(price: 7, count: 11)
]
var coinsDeposited = 0
func dispenseSnack(snack: String) {
print("Dispensing \(snack)")
}
func vend(itemNamed name: String) throws {
guard let item = inventory[name] else {
throw VendingMachineError.InvalidSelection
}
guard item.count > 0 else {
throw VendingMachineError.OutOfStock
}
guard item.price <= coinsDeposited else {
throw VendingMachineError.InsufficientFunds(coinsNeeded: item.price - coinsDeposited)
}
coinsDeposited -= item.price
var newItem = item
newItem.count -= 1
inventory[name] = newItem
print("Dispensing \(name)")
}
}
在vend(itemNamed:)方法的實(shí)現(xiàn)中使用了guard語句來提前退出方法,確保在購買某個物品所需的條件中,有任一條件不滿足時,能提前退出方法并拋出相應(yīng)的錯誤。由于throw語句會立即退出方法,所以物品只有在所有條件都滿足時才會被售出。
- 用 Do-Catch 處理錯誤
可以使用一個do-catch語句運(yùn)行一段閉包代碼來處理錯誤。如果在do子句中的代碼拋出了一個錯誤,這個錯誤會與catch子句做匹配,從而決定哪條子句能處理它。
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
}
使用姿勢如下:
var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.InvalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.OutOfStock {
print("Out of Stock.")
} catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
}
// 打印 “Insufficient funds. Please insert an additional 2 coins.”
buyFavoriteSnack(person:vendingMachine:)函數(shù)在一個try表達(dá)式中調(diào)用,因?yàn)樗軖伋鲥e誤。如果錯誤被拋出,相應(yīng)的執(zhí)行會馬上轉(zhuǎn)移到catch子句中,并判斷這個錯誤是否要被繼續(xù)傳遞下去。如果沒有錯誤拋出,do子句中余下的語句就會被執(zhí)行。”
- 將錯誤轉(zhuǎn)換成可選值
可以使用try?通過將錯誤轉(zhuǎn)換成一個可選值來處理錯誤。如果在評估try?表達(dá)式時一個錯誤被拋出,那么表達(dá)式的值就是nil。例如,在下面的代碼中,x和y有著相同的數(shù)值和等價(jià)的含義:
func someThrowingFunction() throws -> Int {
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
如果someThrowingFunction()拋出一個錯誤,x和y的值是nil。否則x和y的值就是該函數(shù)的返回值。注意,無論someThrowingFunction()的返回值類型是什么類型,x和y都是這個類型的可選類型。例子中此函數(shù)返回一個整型,所以x和y是可選整型。
- defer關(guān)鍵字
defer關(guān)鍵字用來處理類似Ojective C中@try-@catch-@finally中,@finally的作用。
比如,我們打開文件,如果拋出錯誤的話,我們總希望關(guān)閉這個文件句柄。
func contents(of filePath:String) throws -> String{
let file = open(filePath,O_RDWR)
defer {
close(file)
}
//...
}
關(guān)于defer,有兩點(diǎn)需要注意
多個defer會按照逆序的方式執(zhí)行。
當(dāng)你的程序遇到嚴(yán)重錯誤,比如fatalError,或者強(qiáng)制解析nil,或者segfaults的時候,defer的代碼塊并不會執(zhí)行。