Swift中的错误处理ErrorHanding

所属分类:ios | 发布于 2023-01-21 23:23:39

每种语言都会提供错误处理的功能,Swift也不例外,Swift在运行时提供了抛出、捕获、传递和可恢复错误(recoverable erros)的一等支持。

Swift中的错误

在Swift中,错误用遵循Error协议的类型的值来表示。这个空协议表示该类型可以用于错误处理。

Swift的enum类型尤为合适构建一组相关的错误状态,枚举的关联值还可以提供错误的额外信息。

抛出错误

在Swift中,抛出错误使用throw语句,例如下面的代码抛出一个错误,提示贩卖机还需要5个硬币:

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

Swift错误处理

在Swift中,提供了4中错误处理的方式,分别是用throwing函数传递错误、用do-catch语句处理错误、用try?关键字将错误作为可选类型处理、或者用try!关键字断言此错误根本不会发生。

错误示例准备

假如我们有个要从磁盘上的某个文件读取数据并进行处理,该任务会有多种可能失败的情况,如知道路径下文件不存在,文件不具有可读权限,或者文本编码格式不兼容等等。区分这些不同的失败情况可以让程序处理并解决某些错误,然后把它解决不了的错误报告给用户。

在项目的Resouces目录下有个test.txt的文件,里面是一个字符串"hello swift error",我们可使用Bundle.main.url()方法来获取它的url,然后再用String(contentsOf: URL)来读取数据,在这个示例中,会发生两种错误:

1、用户给定的文件可能不存在

2、调用String(contentsOf: URL)构造器初始化对象是,这个构造器会抛出异常,需要我们手动处理。

错误示例

准备一段代码如下:

func loadFile() {
    if let fileURL = Bundle.main.url(forResource: "subtest",
                                     withExtension: "txt",
                                     subdirectory: "txts/subTxts") {
        let c1 = try String(contentsOf: fileURL)
        print("c1:", c1)
    } else {
        print("error2:", "url not found")
    }
}

loadFile()

这段代码定义了一个loadFile()方法用来加载本地文件夹下的一个文件,String(contentsOf:)会抛出一个异常,所以要在它的前面加上try,但是上面的代码并没有处理异常,所以该段代码会编译失败。我们必须处理抛出的异常。

错误处理的四种方式

1、用throwing函数传递错误

1.1、throwing函数

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

一个标有throws关键字的函数被称为throwing函数。

如果这个函数指明了返回类型,throws关键字需要写在返回箭头(->)的前面。

func canThrowErrors() throws -> String () { }

一个throwing函数可以在其内部抛出错误,并将错误传递到函数被调用的作用域。

1.2 用throwing函数处理示例错误

// 使用throwing函数处理异常
func loadFile1() throws {
    if let fileURL = Bundle.main.url(forResource: "subtest",
                                     withExtension: "txt",
                                     subdirectory: "txts/subTxts") {
        // 可以重新给fileURL赋值一个不存在的URL,以便抛出错误
        // let fileURL = URL(string: "xxxx")!
        let c1 = try String(contentsOf: fileURL)
        print("c1:", c1)
    } else {
        print("error2:", "url not found")
    }
}

do {
    try loadFile1()
} catch (let error) {
    print("error:", error.localizedDescription)
}

给loadFile()函数加上throws关键字,让它变成一个throwing函数,就可以将错误传递到调用方,由调用方来处理。

最终调用方还是需要使用下面的三种方法将错误处理掉。

二、用do-catch处理错误

使用一个do-catch语句运行一段闭包代码来处理错误。如果在do子句中的代码抛出了一个错误,这个错误会与catch子句做匹配,从而决定哪条子句能处理它。

do-catch常见语法:

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

上面的例子我们已经见识过do-catch语句处理错误,下面我们不使用throwing函数,使用do-catch语句来处理错误

// 使用do-catch语句处理异常
func loadFile2() {
    if let fileURL = Bundle.main.url(forResource: "subtest",
                                     withExtension: "txt",
                                     subdirectory: "txts/subTxts") {
        do {
            // 可以重新给fileURL赋值一个不存在的URL,以便抛出错误
            // let fileURL = URL(string: "xxxx")!
            let c2 = try String(contentsOf: fileURL)
            print("c2:", c2)
        } catch (let error) {
            print("error:", error.localizedDescription)
        }
        
    } else {
        print("error2:", "url not found")
    }
}

loadFile2()

去掉throws关键字,将要抛出错误的语句用do语句包裹,在catch子句中处理异常。

三、使用try?关键字将错误转换成可选值

通过使用try?关键字将错误转换成一个可选值来处理错误。

如果是在计算try?表达式时抛出错误,那么该表达式的结果就是nil。

// 使用try?关键字将错误转换成可选值
func loadFile3() {
    if let fileURL = Bundle.main.url(forResource: "subtest",
                                     withExtension: "txt",
                                     subdirectory: "txts/subTxts") {
        let c3 = try? String(contentsOf: fileURL)
        print("c3:", c3!) // 使用c3需要强制解包, 当发生错误时c3的值为nil。
    } else {
        print("error2:", "url not found")
    }
}

loadFile3()

四、使用try!关键字禁用错误传递

有时你知道,讴歌throwing函数实际上在运行时不会抛出错误,这种情况下,可以在表达式前面写try!来禁用错误传递,这样会把调用调用包装在一个不会有错误抛出的运行时断言中。

此时如果真的抛出了错误,你就会得到一个运行时错误。

// 使用try!关键字将错误转换成可选值
func loadFile4() {
    if let fileURL = Bundle.main.url(forResource: "subtest",
                                     withExtension: "txt",
                                     subdirectory: "txts/subTxts") {
        let c4 = try! String(contentsOf: fileURL)
        print("c4:", c4) // 使用try!关键字后,这里也不用强制解包了
    } else {
        print("error2:", "url not found")
    }
}

loadFile4()

 

文哥博客(https://wenge365.com)属于文野个人博客,欢迎浏览使用

联系方式:qq:52292959 邮箱:52292959@qq.com

备案号:粤ICP备18108585号 友情链接