错误处理(学习笔记)
环境Xcode 11.0 beta4 swift 5.1
-
错误表现和抛出
-
在
swift
中,错误由符合Error
协议的类型值表示// 示例 enum VendingMachineError: Error { case invalidSelection case insufficientFunds(coinsNeeded: Int) case outOfStock } // 抛出错误 throw VendingMachineError.insufficientFunds(coinsNeeded: 5)
-
-
错误处理
- 在 Swift 中有四种处理错误的方式:一、从函数中把错误传递出来;二、用
do-catch
语句;三、作为可选值处理;四、用断言 - 在 Swift 中错误处理与其它语言(包括OC)的异常类似,Swift 中错误处理不会涉及展开堆栈的调用,而堆栈的调用消耗是很大的,从性能的角度出发
throw
语句的性能与return
性能是一样的 -
函数抛出错误,只有声明了
throws
的函数才能往外抛出错误,如果不是则抛出的错误必须在函数内部处理func canThrowErrors() throws -> String // func cannotThrowErrors() -> String
-
示例代码
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)") } } 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) // 如果这里有错误,将会向外层抛 }
-
可抛出的初始化器也可以向外抛出错误
struct PurchasedSnack { let name: String init(name: String, vendingMachine: VendingMachine) throws { try vendingMachine.vend(itemNamed: name) self.name = name } }
-
用
do-catch
处理错误,一般格式如下do { try expression // statements } catch pattern1 { // statements } catch pattern2 where condition { // statements } catch { // statements } // catch 后的 pattern1 表示这个大括号内处理 pattern1 类型的错误,如果省略不写表示处理任何类型的错误且把错误绑定到一个常量 `error`
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?
将错误转换为可选值,如果有错误抛出,计算try?
表达式的值为nil
swift func someThrowingFunction() throws -> Int { // .... } let x = try? someThrowingFunction() let y: Int? do { y = try someThrowingFunction() }catch { y = nil } // 如果函数 someThrowingFunction 抛出错误,则 x 的值为 nil;否则 x、y 就是函数返回的值,且此时x、y是可选值
- 如果想用
try?
准确处理所有错误,如下示例swift func fetchData() -> Data? { if let data = try? fecthDataFromDisk() { return data } if let data = try? fetchDataFromDisk() { return data } return nil }
- 可以用
- 禁止错误传递,可以用
try!
, 如果值是nil
,则会抛出运行时错误
// 示例 let photo = try! loadImage(atPath: "./Resources/John Appleseed.jpg")
- 在 Swift 中有四种处理错误的方式:一、从函数中把错误传递出来;二、用
-
指定清理行为
- 在当前代码块结束之前,可以用
defer
语句执行一组语句,做任何必需的清理操作,语句的执行无关是抛出错误或者是return
break
关键字之类 -
defer 语句源代码的顺序与执行的顺序相反,即前面的后执行,后面的先执行
func processFile(filename: String) throws { if exists(filename) { let file = open(filename) defer { close(file) } while let line = try file.readline() { // Work with the file. } // close(file) is called here, at the end of the scope. } }
- 在当前代码块结束之前,可以用
请发表评论