[Swift 开发] 异常俘获 Do_Try_Catch

定义抛出Throw类型

表示和抛出错误 在 Swift 中,错误由符合 Error 协议的类型值表示。这个空协议表明一个类型可用于错误处理。

Swift 枚举特别适合对一组相关的错误条件进行建模,关联的值允许传达有关要传达的错误性质的附加信息。

例如,您可以通过以下方式表示在游戏中操作自动售货机的错误条件:

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

抛出错误让您表明发生了意外情况,正常的执行流程无法继续。使用 throw 语句抛出错误。例如,下面的代码会抛出一个错误,表明自动售货机还需要 5 个额外的硬币:

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

函数中Throw的运用

要指示函数、方法或初始化程序可以抛出错误,请在函数声明中的参数后面编写 throws 关键字。

标有 throws 的函数称为 throwing 函数。

如果函数指定了返回类型,则在返回箭头 (->) 之前编写 throws 关键字。

    func canThrowErrors() throws -> String

    func cannotThrowErrors() -> String

在下面的示例中,VendingMachine 类有一个 vend(itemNamed:) 方法,如果请求的项目不可用、缺货或成本超过当前存入的金额,则该方法会抛出适当的 VendingMachineError

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

vend(itemNamed:) 方法的实现使用保护语句提前退出该方法并在不满足购买零食的任何要求时抛出适当的错误。因为 throw 语句会立即转移程序控制,所以只有满足所有这些要求才会出售项目。

因为 vend(itemNamed:) 方法会传播它抛出的任何错误,所以任何调用此方法的代码都必须处理这些错误——使用 do-catch 语句、try? 或 try!——或者继续传递它们。

例如,下例中的 buyFavoriteSnack(person:vendingMachine:) 也是一个抛出函数,并且 vend(itemNamed:) 方法抛出的任何错误都会传递到 buyFavoriteSnack(person:vendingMachine:)

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

在这个例子中,buyFavoriteSnack(person: vendingMachine:) 函数查找给定人最喜欢的零食,并尝试通过调用 vend(itemNamed:) 方法为他们购买。由于 vend(itemNamed:) 方法可能会抛出错误,因此在调用它时会在其前面加上 try 关键字。 抛出初始化器可以以与抛出函数相同的方式传递错误。

例如,下面清单中的 PurchasedSnack 结构的初始化程序调用一个抛出函数作为初始化过程的一部分,它通过将它们传递给它的调用者来处理它遇到的任何错误。

    struct PurchasedSnack {
        let name: String
        init(name: String, vendingMachine: VendingMachine) throws {
            try vendingMachine.vend(itemNamed: name)
            self.name = name
        }
    }

使用 Do_Try_Catch 处理错误

您可以使用 do-catch 语句通过运行代码块来处理错误。如果 do 子句中的代码抛出错误,则它会与 catch 子句进行匹配,以确定它们中的哪一个可以处理错误。

以下是 do-catch 语句的一般形式:

    do {
        try expression
        statements
    } catch pattern 1 {
        statements
    } catch pattern 2 where condition {
        statements
    } catch pattern 3, pattern 4 where condition {
        statements
    } catch {
        statements
    }

在 catch 之后编写一个模式来指示该子句可以处理哪些错误。如果 catch 子句没有模式,则该子句匹配任何错误并将错误绑定到名为 error 的本地常量。

例如,以下代码与 VendingMachineError 枚举的所有三种情况相匹配。

    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).")
    }
    // Prints "Insufficient funds. Please insert an additional 2 coins."

在上面的示例中,在 try 表达式中调用了 buyFavoriteSnack(person:vendingMachine:) 函数,因为它会抛出错误。如果抛出错误,则执行立即转移到 catch 子句,它决定是否允许继续传递。如果没有匹配的模式,错误会被最后的 catch 子句捕获并绑定到一个局部错误常量。如果没有抛出错误,则执行 do 语句中的其余语句。

捕获几个相关错误的另一种方法是在 catch 之后列出它们,用逗号分隔。例如

    func eat(item: String) throws {
        do {
            try vendingMachine.vend(itemNamed: item)
        } catch VendingMachineError.invalidSelection, VendingMachineError.insufficientFunds, VendingMachineError.outOfStock {
            print("Invalid selection, out of stock, or not enough money.")
        }
    }

eat(item:) 函数列出要捕获的自动售货机错误,其错误文本对应于该列表中的项目。如果抛出列出的三个错误中的任何一个,这个 catch 子句通过打印一条消息来处理它们。 其它的异常会继续上传递Throw

将Throw转换为Optional类型

你用try?通过将错误转换为可选值来处理错误。如果在评估 try 时抛出错误?表达式,表达式的值为 nil。例如,在以下代码中,x 和 y 具有相同的值和行为:

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

    let x = try? someThrowingFunction()

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

如果 someThrowingFunction() 抛出错误,则 x 和 y 的值为零。否则,x 和 y 的值就是函数返回的值。请注意, x 和 y 是 someThrowingFunction() 返回的任何类型的可选项。这里函数返回一个整数,所以 x 和 y 是可选的整数。

使用try?当您想以相同的方式处理所有错误时,让您可以编写简洁的错误处理代码。例如,以下代码使用多种方法来获取数据,如果所有方法都失败,则返回 nil。

    func fetchData() -> Data? {
        if let data = try? fetchDataFromDisk() { return data }
        if let data = try? fetchDataFromServer() { return data }
        return nil
    }
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值