错误处理

优质
小牛编辑
130浏览
2023-12-01

错误处理

1. 错误表示

  • Swift中如果我们要定义一个表示错误的类型非常简单, 只要遵循Error协议就可以了, 我们通常用枚举或结构体来表示错误类型, 枚举可能用的多些, 因为它能更直观的表达当前错误类型的每种错误细节.

      enum VendingMachineError: Error {
          case invalidSelection
          case insufficientFunds(coinsNeeded: Int)
          case outOfStock
      }
    
  • 当然通过这样的方式, 其实表达错误信息方面还是不够详细, 如果我们想要展示更多的错误信息, 系统给我提供了LocalizedError协议, 以便我们来更清晰的表达一个错误


2. 如何抛出错误

  • 函数、方法和初始化器都可以抛出错误. 需要在参数列表后面, 返回值前面加throws关键字.
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String

e.g.

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

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)")
    }
}


let favoriteSnacks = [
    "Alice": "Chips",
    "Bob": "Libcorice",
    "Eve": "Pretzels"
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
    let snackName = favoriteSnacks[person] ?? "Candy Bar"
    try vendingMachine.vend(itemNamed: snackName)
}

3. 使用Do-Catch做错误处理

  • Swift中我们使用do-catch块对错误进行捕获, 当我们在调用一个throws声明的函数或方法时, 我们必须把调用语句放在do语句块中, 同时do语句块后面紧接着使用catch语句块.
do {
    try <#throwing expression#>
    <#statements#>
} catch <#pattern#> {
    <#statements#>
} catch <#pattern#> where <#condition#> {
    <#statements#>
} catch {
    <#statements#>
}

e.g.

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("insufficientFunds funds. Please insert an additional \(coinsNeeded) coins")
} catch {
    print("Unexpected error: \(error).")
}

4. try?

  • try?会将错误转换为可选值, 当调用try?+ 函数或方法语句时候, 如果函数或方法抛出错误, 程序不会崩溃, 而返回一个nil, 如果没有抛出错误则返回可选值.

e.g.

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

let x = try? someThrowingFunction()

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

5. try!

  • 如果确定一个函数或者方法不会抛出错误, 可以使用try!来中断错误的传播.但是如果错误真的发生了, 就会得到一个运行时错误.

e.g.

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

6. try

  • 没有?!修饰, 当捕获到异常直接向上抛出, 一般配合do-catch使用

7. defer 指定退出的清理动作

  • defer关键字: defer block里的代码会在函数return之前执行, 无论函数是从哪个分支return的, 还是有throw, 还是自然而然的走到最后一行.
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 scope.
}