错误处理
错误处理是响应错误以及从错误中返回的过程。swift提供第一类错误支持,包括在运行时抛出,捕获,传送和控制可回收错误。
一些函数和方法不能总保证能够执行所有代码或产生有用的输出。可空类型用来表示值可能为空,但是当函数执行失败的事后,可空通常可以用来确定执行失败的原因,因此代码可以正确地响应失败。在Swift中,这叫做抛出函数或者抛出方法。
错误的表示
在Swift中,错误用符合ErrorType
协议的值表示。 Swift枚举特别适合把一系列相关的错误组合在一起,同时可以把一些相关的值和错误关联在一起。因此编译器会为实现ErrorType
协议的Swift枚举类型自动实现相应合成。
// 模拟自动售卖机的错误情况 enum VendingMachineError: ErrorType { // 商品不存在时 case InvalidSelection // 投入的钱不够时 case InsufficientFunds(required: Double) // 商品卖完了 case OutOfStock }
错误抛出 通过在函数或方法声明的参数后面加上throws
关键字,表明这个函数或方法可以抛出错误。如果指定一个返回值,可以把throws
关键字放在返回箭头(->)的前面。
func canThrowError() throw -> String func cannotThrowError() -> String
当调用一个抛出函数的时候,在调用前面加上try
。这个关键字表明函数可以抛出错误。
let favoriteSnacks = [ "Alice": "Chips", "Bob": "Licorice", "Eve": "Pretzels", ] func buyFavoriteSnack(person: String) throws { let snackName = favoriteSnacks[person] ?? "Candy Bar" try vend(itemName: snackName) }
捕捉和处理错误
使用do-catch语句来就捕获和处理错误
do { try vend(itemName: "Candy Bar") // 如果vend会抛出错误的话,会传递到catch中 // 可以在catch里处理发生错误后应做的事情,不会造成运行时错误 } catch VendingMachineError.InvalidSelection { print("Invalid Selection") } catch VendingMachineError.InsufficientFunds(let amountRequired) { print("Insufficient funds. Please insert an additional $\(amountRequired).") } catch VendingMachineError.OutOfStock { print("Out of Stock") } // Insufficient funds. Please insert an additional $0.25.
注意:Swift中的错误处理和其他语言中的异常处理很像,使用了try
、catch
和throw
关键字。但是和这些语言——包括Objective-C——不同的是,Swift不会展开调用堆栈,那会带来很大的性能损耗。因此,在Swift中throw
语句的性能可以做到几乎和return
语句一样。
禁止错误传播
在运行时,有几种情况抛出函数事实上是不会抛出错误的。在这几种情况下,你可以用forced-try
表达式来调用抛出函数或方法,即使用try!
来代替try
。
通过try!
来调用抛出函数或方法禁止了错误传送,并且把调用包装在运行时断言,这样就不会抛出错误。如果错误真的抛出了,会触发运行时错误。
enum CustomError: ErrorType { case SomeError } func willOnlyThrowIfTrue(value: Bool) throws { if value { throw CustomError.SomeError } } do { try willOnlyThrowIfTrue(true) } catch { print("Error") } try! willOnlyThrowIfTrue(true) // 运行时错误
收尾操作
使用defer语句来在执行一系列的语句。这样不管有没有错误发生,都可以执行一些必要的收尾操作。包括关闭打开的文件描述符以及释放所有手动分配的内存。
defer
语句把执行推迟到退出当前域的时候。defer
语句包括defer
关键字以及后面要执行的语句。被推迟的语句可能不包含任何将执行流程转移到外部的代码,比如break
或者return
语句,或者通过抛出一个错误。被推迟的操作的执行的顺序和他们定义的顺序相反,也就是说,在第一个defer
语句中的代码在第二个defer
语句中的代码之后执行。
func processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try file.readline() { // 写文件 } // defer中的close(file) 会在最后调用。不管有没有抛出错误 } }