美文网首页
Swift 异常处理

Swift 异常处理

作者: 張無忌 | 来源:发表于2017-09-28 11:20 被阅读59次

    异常的由来

    在写代码的过程中,我们不能保证自己的每一行代码,都能够正确地执行。不能保证每一个函数,都会返回我们所期望的值。所以很多编程语言,都引入了异常处理机制,来让我们得知执行失败的原因,从而可以做出应对。

    举例:在读取某个磁盘中的文件时,可能遇到打开磁盘失败文件不存在等错误。

    使用可选来处理简单的异常

    在 Swift 中有可选的机制,可以解决函数返回不成功的问题,我们经常写出如下代码:

    /// 计算
    func caculator() -> Double? {
        // XXX
    }
    

    然后调用这个函数

    if let result = caculator() {
        print(result)
    } else {
        print("计算错误")
    }
    

    通过可选机制,可以应对一些简单的错误处理。当返回了正确的值,我们可以进行下一步操作,当返回nil,则表示计算过程中发生了错误。但是当出现问题的原因比较多的时候,nil的表现力就显得很弱。所以,接下来介绍异常处理

    认识异常处理

    假设有这样一个场景:汽车启动,当使用的钥匙正确,才能开启汽车。当燃油大于5升才可以成功启动,否则启动不成功。

    我们首先定义一个枚举,来描述汽车启动失败可能的每种情况。针对燃油不足的情况,我们还设置了关联值来提醒用户还差多少油才能启动。

    // 描述汽车出问题的各种情况
    enum CarError: Error {
        case keyError    // 钥匙不对
        case outOfFuel(fuelNeed: Double)    // 燃油不足
    }
    

    接着定义汽车模型。

    struct Car {
        // 当前燃油
        var fuelInLitre: Double
        // 汽车的钥匙
        var keyName: String
    
        // 启动汽车
        func start() throws -> String {
            guard keyName == "trueKey" else {
                throw CarError.keyError
            }
            guard fuelInLitre > 5 else {
                throw CarError.outOfFuel
            }
            return "启动成功"
        }
    }
    

    假设正确的钥匙名称为trueKey。这里使用了throws关键字,来表示start方法在执行过程中,可能会抛出异常。throws需要写在->前面。

    接着,尝试调用以上的代码。

    let car = Car(fuelInLitre: 6, keyName: "trueKey")
    do {
        let message = try car.start()
        print(message)
    } catch CarError.keyError {
        print("钥匙不正确")
    } catch CarError.outOfFuel(let fuelNeed) {
        print("燃油不足,还需要:\(fuelNeed)")
    } catch {
        print("发生了其他错误")
    }
    

    我们先是定义了一个Car的实例,接着尝试调用start方法,因为这个方法可能会抛出异常,所以必须包含在do的代码块里面,在调用方法前面加上try关键字。调用完毕后,我们就当这个方法能调用成功,继续写成功之后的代码,输出启动信息。

    接着使用catch关键字,来捕获可能抛出的异常,并对异常进行处理,我们可能会打印错误日志,或者提示一些有用的信息。

    事实上我们只定义了两种可能发生的异常,但是编译时Xcode 9会报错,必须在最后面再加一个catch来表示发了生其他的错误,有点类似于switch语句最后那个default

    其他补充

    除了try关键字,还有try?try!关键字。try?和可选机制很类似,当方法抛出异常,就会返回一个nil
    还是上面汽车启动的例子,我们可以这样写:

    let car = Car(fuelInLitre: 3, keyName: "trueKey")
    if let message = try? car.start() {
        print(message)
    } else {
        print("启动错误,具体原因不知")
    }
    

    就不去判断那么多的异常情况,如果返回nil,就代表发生了异常,具体原因不关心。

    try!关键字和强制解包很类似,当你认为某个方法一定能执行成功不会抛出异常时,就用它。但是万一抛出了异常,那么程序就崩溃。

    let car = Car(fuelInLitre: 6, keyName: "trueKey")
    // 强制执行,如果失败,程序直接崩溃
    let message = try! car.start()
    print(message)
    

    在介绍defer关键字之前,假设:当我们尝试开启一辆汽车,无论启动成功,或是启动失败,都会在最后关闭车门。首先为Car模型新增一个方法closeDoor()

    func closeDoor() {
        print("关好车门")
    }
    

    然后使用defer关键字,调用。

    // 打开了汽车的车门
    let car = Car(fuelInLitre: 6, keyName: "trueKey")
    // 延时执行,无论 start 方法执行成功或者失败,我们都调用方法来关闭车门
    defer {
        car.closeDoor()
    }
    do {
        let message = try car.start()
        print(message)
    } catch CarError.keyError {
        print("钥匙不正确")
    } catch CarError.outOfFuel(let fuelNeed) {
        print("燃油不足,还需要:\(fuelNeed)")
    } catch {
        print("发生了其他错误")
    }
    

    defer关键字,当你的代码无论是正常的执行了,或者是抛出了异常,都会去调用defer代码块里面的语句,多用于做一些清理的工作。

    上面的例子比较牵强,另外举一个十分常见的场景:我们尝试打开磁盘,去读取某个文件时,无论读取成功还是失败,都会在最后关闭磁盘。

    相关文章

      网友评论

          本文标题:Swift 异常处理

          本文链接:https://www.haomeiwen.com/subject/lngxextx.html