表示和抛出错误
// 抛出一个错误允许你明确某些意外的事情发生了并且正常的执行流不能继续下去。你可以使用 throw 语句来抛出一个错误
throw VendingMachineError.InsufficientFunds(coinsNeeded: 5)
处理错误
//Swift 中的错误处理, try, catch 和 throw 的使用与其他语言中的异常处理很相仿。不同于许多语言中的异常处理——包括 Objective-C ——Swift 中的错误处理并不涉及调用堆栈展开,一个高占用过程。因此, throw 语句的性能特征与 return 比不差多少。
//在 Swift 中有四种方式来处理错误。
//你可以将来自函数的错误传递给调用函数的代码中,使用 do-catch 语句 来处理错误,把错误作为可选项的值,或者错误不会发生的断言。每一种方法都在下边的章节中有详细叙述。
//当函数抛出一个错误,它就改变了你程序的流,所以能够快速定位错误就显得格外重要。要定位你代码中的这些位置,使用** try 关键字——或者 try? 或 try!** 变体——放在调用函数、方法或者会抛出错误的初始化器代码之前。这些关键字在下面的章节中有详细的描述。
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String
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 var 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
--item.count
inventory[name] = item
dispenseSnack(name)
}
}
let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}
// Dispensing Chips
// 在这个栗子中, buyFavoriteSnack(person:vendingMachine:)函数查找给定人的最爱零食并且尝试通过调用 vend(itemNamed:)方法来购买它们。由于 vend(itemNamed:) 方法会抛出错误,调用的时候要在前边用 try关键字。
使用 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)
// Enjoy delicious snack
} 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.")
}
// prints "Insufficient funds. Please insert an additional 2 coins."
转换错误为可选项
func someThrowingFunction() throws -> Int {
// ...
}
// 下面两段代码是等价的
let x = try? someThrowingFunction()
let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}
func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}
let photo = try! loadImage("./Resources/John Appleseed.jpg")
取消错误传递
// 事实上有时你已经知道一个抛出错误或者方法不会在运行时抛出错误。在这种情况下,你可以在表达式前写 try!来取消错误传递并且把调用放进不会有错误抛出的运行时断言当中。如果错误真的抛出了,你会得到一个运行时错误。
比如说,下面的代码使用了 loadImage(_:)函数,它在给定路径下加载图像资源,如果图像不能被加载则抛出一个错误。在这种情况下,由于图像跟着应用走,运行时不会有错误抛出,所以取消错误传递是合适的。
let photo = try! loadImage("./Resources/John Appleseed.jpg")
#### 指定清理操作 (defer)
// 使用 defer语句来在代码离开当前代码块前执行语句合集。这个语句允许你做任何无论执行如何离开当前代码块都必须要做的清理工作——无论是因为抛出了错误还是因为 return或者 break这样的语句。比如,你可以使用 defer语句来保证文件描述符都关闭并且手动指定的内存到被释放。
func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// Work with the file.
}
// close(file) is called here, at the end of the scope.
}
}