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。以下写法是完全等价的。其中xy都是可选整形。

func someThrowingFunction() throws -> Int {
    // ...
}

let x = try? someThrowingFunction()

let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}

禁用错误传递

使用try!可以禁止错误传递,如果发生错误,会直接变成断言函数,直接变成运行时错误。只有在错误完全不会出现的时候才可以使用。

指定清理操作

defer语句在离开代码块前进行调用,不论是returnbreak还是抛出一个错误。延迟执行语句不可以包括任何控制转移语句,包括returnbreak以及抛出一个错误。
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按照声明的顺序从后到前进行执行。