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