Swift

错误处理

本文详细介绍了 Swift 中的错误处理机制,包括错误的表示与抛出、使用 throwing 函数传递错误、Do-Catch 捕获错误、将错误转换为可选值以及禁用错误传递。

错误处理(Error Handling)是响应错误并从中恢复的过程。Swift 提供了一流的错误处理支持,包括在运行时抛出、捕获、传递和操作可恢复错误。

表示与抛出错误

在 Swift 中,错误用符合 Error 协议的类型的值来表示。枚举特别适合构建一组相关的错误状态。

enum VendingMachineError: Error {
    case invalidSelection
    case insufficientFunds(coinsNeeded: Int)
    case outOfStock
}

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

处理错误

fSwift 提供了四种处理错误的方式:

  1. 将错误传递给调用该函数的代码。
  2. 使用 do-catch 语句处理错误。
  3. 将错误作为可选值处理。
  4. 断言此错误根本不会发生。

用 throwing 函数传递错误

为了表示一个函数、方法或构造器可以抛出错误,在参数列表后加上 throws 关键字。

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 语句运行一段代码。如果 do 子句中的代码抛出了错误,它会与 catch 子句进行匹配,以确定哪一个可以处理该错误。

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack(person: "Alice", vendingMachine: vendingMachine)
    print("Success! Yum.")
} 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.")
} catch {
    print("Unexpected error: \(error).")
}

将错误转换成可选值

使用 try? 将错误转换为可选值。如果函数抛出错误,表达式的值为 nil

func fetchData() -> Data? {
    if let data = try? fetchDataFromDisk() { return data }
    if let data = try? fetchDataFromServer() { return data }
    return nil
}

禁用错误传递

如果你确定某个 throwing 函数在运行时不会抛出错误,可以使用 try!。这会将调用包装在运行时断言中,如果真的抛出错误,应用会崩溃。

let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")

指定清理操作

使用 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) 会在这里被调用,就在作用域结束之前
    }
}
在 GitHub 上编辑

上次更新于