Swift文档Chapter 17 错误处理
**错误处理(Error handling)**是响应错误以及从错误中恢复的过程。
表示与抛出错误
在Swift中,错误遵循Error
协议类型的值表示。这个空协议可以保证这个类可以进行错误处理。
枚举类型适合构建一组相关的错误状态。枚举的关联值可以为提供错误状态额外的信息。
enum VendingMachineError: Error {
case invalidSelection //选择无效
case insufficientFunds(coinsNeeded: Int) //金额不足
case outOfStock //缺货
}
当需要抛出错误的时候,需要使用throw
语句。
处理错误
错误的处理一共有4种方式:
- 把函数抛出的错误传递给调用此函数的代码;
do-catch
处理错误;- 将错误作为可选类型处理;
- 断言此错误根本不会发生。
用throwing
函数传递错误
在函数声明的参数后加入throws
关键字,这个函数就是一个throwing
函数。如果这个函数指明了返回值类型,那么throws
关键字应当在->
之前。
throwing函数可以在其内部抛出错误,并将错误传递到函数调用时的作用域。非throwing函数只能在函数内部处理错误。
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 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)")
}
}
在上面的例子中,guard
语句可以保证任何一个条件不满足时都可以提前退出方法并抛出对应的错误。throw
语句会立刻退出方法。
这个方法会传递出他抛出的任何错误。因此我们如果要调用它,就必须使用do-catch
或者try?
以及try!
函数进行处理,或者将错误继续传递下去。throwing
构造器也可以传递错误。
do-catch
处理错误
do-catch
语句的一般形式:
do {
try expression
statements
} catch pattern 1 {
statements
} catch pattern 2 where condition {
statements
} catch {
statements
}
do
语句中抛出的错误会和catch
语句匹配。如果catch
不指定匹配的错误,那么可以和所有的错误匹配。如果所有catch
子句都未处理错误,错误就会传递到周围的作用域。如果错误传递到了顶层作用域却依然没有被处理,你会得到一个运行时错误。
将错误转换为可选值
使用try?
可以将错误转换为可选值,如果在计算try?
时抛出错误,那么表达式的结果就是nil
。以下写法是完全等价的。其中x
和y
都是可选整形。
func someThrowingFunction() throws -> Int {
// ...
}
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
禁用错误传递
使用try!
可以禁止错误传递,如果发生错误,会直接变成断言函数,直接变成运行时错误。只有在错误完全不会出现的时候才可以使用。
指定清理操作
defer
语句在离开代码块前进行调用,不论是return
,break
还是抛出一个错误。延迟执行语句不可以包括任何控制转移语句,包括return
,break
以及抛出一个错误。
defer
语句的声明是defer
关键词后直接加上花括号,花括号内定义语句。
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// 处理文件。
}
// close(file) 会在这里被调用,即作用域的最后。
}
}
defer
按照声明的顺序从后到前进行执行。