Swift错误处理
可能发生的错误分为两大类:可恢复的错误和不可恢复的错误。
常见的可恢复错误:
- 试图打开不存在的文件
- 试图和下线的服务器通信
- 试图在设备没有网络连接时通信
常见的不可恢复错误:
- 强制展开值为nil的可空实例
- 数组越界
说明:Swift中的错误处理涉及到错误处理模式,这会用到Cocoa和OC中NSError。
表示并抛出错误
在 Swift 中,错误用符合Error
协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
- 枚举类型尤为适合构建一组相关的错误状态,枚举的关联值还可以提供错误状态的额外信息。
- 抛出错误用throw关键字
示例:
1 | enum VendingMachineError: Error { |
处理错误方式
throwing函数传递错误
- 声明的参数列表后加
thorws
关键字的函数被称为throwing
函数,表示这种函数可以抛出错误。如果函数有返回值,throws
需要写在->
前。 - 一个 throwing 函数可以在其内部抛出错误,并将错误传递到函数被调用时的作用域。
- 只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38struct 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)")
}
}- 声明的参数列表后加
do-catch处理错误
do-catch语句一般形式:
1
2
3
4
5
6
7
8do{
try expression
statements
} catch pattern 1{
statements
} catch pattern 2 where condition{
statements
}catch后面跟一个匹配模式来表明这个句子能处理什么样的错误。如果一条catch子句没有指定匹配模式,那么这条子句可以匹配任何错误,并且把错误绑定到一个名字为error的局部常量。
catch子句不必将do子句中的代码所抛出的每一个可能的错误都做处理。如果所有catch子句都未处理错误,错误就会传递到周围的作用域。
在do中至少有一个try语句。
将错误转换成可选值
可以使用
try?
通过将错误转换成一个可选值来处理错误。如果在评估try?
表达式时一个错误被抛出,那么表达式的值就是nil
。1
2
3
4
5func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}禁用错误传递
有时你知道某个
throwing
函数实际上在运行时是不会抛出错误的,在这种情况下,你可以在表达式前面写try!
来禁用错误传递,这会把调用包装在一个不会有错误抛出的运行时断言中。如果真的抛出了错误,你会得到一个运行时错误。1
let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
指定清理操作
可以使用defer
语句在即将离开当前代码块时执行一系列语句。该语句让你能执行一些必要的清理工作,不管是以何种方式离开当前代码块的——无论是由于抛出错误而离开,还是由于诸如return
或者break
的语句。例如,你可以用defer
语句来确保文件描述符得以关闭,以及手动分配的内存得以释放。
defer
语句将代码的执行延迟到当前的作用域退出之前。该语句由defer
关键字和要被延迟执行的语句组成。延迟执行的语句不能包含任何控制转移语句,例如break
或是return
语句,或是抛出一个错误。延迟执行的操作会按照它们被指定时的顺序的相反顺序执行——也就是说,第一条defer
语句中的代码会在第二条defer
语句中的代码被执行之后才执行,以此类推。
即使没有涉及到错误处理,你也可以使用defer
语句。
1 | func processFile(filename: String) throws { |