Swift基础-14(错误处理)

1.表示并抛出错误

在 Swift 中,错误用符合Error协议的类型的值来表示。这个空协议表明该类型可以用于错误处理。
Swift 的枚举类型尤为适合构建一组相关的错误状态,枚举的关联值还可以提供错误状态的额外信息。

enum VendingMachineError : Error {
    case invalidSelection                    //选择无效
    case insufficientFunds(coinsNeeded: Int) //金额不足
    case outOfStock                          //缺货
}
2.处理错误

某个错误被抛出时,附近的某部分代码必须负责处理这个错误,例如纠正这个问题、尝试另外一种方式、或是向用户报告错误。
Swift 中有4种处理错误的方式。你可以把函数抛出的错误传递给调用此函数的代码、用do-catch语句处理错误、将错误作为可选类型处理、或者断言此错误根本不会发生。

注意
Swift 中的错误处理和其他语言中用try,catch和throw进行异常处理很像。和其他语言中(包括 Objective-C )的异常处理不同的是,Swift 中的错误处理并不涉及解除调用栈,这是一个计算代价高昂的过程。就此而言,throw语句的性能特性是可以和return语句相媲美的。

  • 用 throwing 函数传递错误
    为了表示一个函数、方法或构造器可以抛出错误,在函数声明的参数列表之后加上throws关键字。一个标有throws关键字的函数被称作throwing 函数。如果这个函数指明了返回值类型,throws关键词需要写在箭头(->)的前面。
func canThrowErrors() throws -> String
func cannotThrowErrors() -> String

注意
只有 throwing 函数可以传递错误。任何在某个非 throwing 函数内部抛出的错误只能在函数内部处理。”

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 dispenseSnack(snack: String) {
      print("Dispensing \(snack)")
  }
  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)")
  }
}

在vend(itemNamed:)方法的实现中使用了guard语句来提前退出方法,确保在购买某个物品所需的条件中,有任一条件不满足时,能提前退出方法并抛出相应的错误。由于throw语句会立即退出方法,所以物品只有在所有条件都满足时才会被售出。

  • 用 Do-Catch 处理错误
    可以使用一个do-catch语句运行一段闭包代码来处理错误。如果在do子句中的代码抛出了一个错误,这个错误会与catch子句做匹配,从而决定哪条子句能处理它。
do {
    try expression
    statements
} catch pattern 1 {
    statements
} catch pattern 2 where condition {
    statements
}

使用姿势如下:

var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited = 8
do {
    try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} 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.")
}
// 打印 “Insufficient funds. Please insert an additional 2 coins.”

buyFavoriteSnack(person:vendingMachine:)函数在一个try表达式中调用,因为它能抛出错误。如果错误被抛出,相应的执行会马上转移到catch子句中,并判断这个错误是否要被继续传递下去。如果没有错误抛出,do子句中余下的语句就会被执行。”

  • 将错误转换成可选值
    可以使用try?通过将错误转换成一个可选值来处理错误。如果在评估try?表达式时一个错误被抛出,那么表达式的值就是nil。例如,在下面的代码中,x和y有着相同的数值和等价的含义:
func someThrowingFunction() throws -> Int {
}

let x = try? someThrowingFunction()
let y: Int?
do {
    y = try someThrowingFunction()
} catch {
    y = nil
}

如果someThrowingFunction()抛出一个错误,x和y的值是nil。否则x和y的值就是该函数的返回值。注意,无论someThrowingFunction()的返回值类型是什么类型,xy都是这个类型的可选类型。例子中此函数返回一个整型,所以x和y是可选整型

  • defer关键字
    defer关键字用来处理类似Ojective C中@try-@catch-@finally中,@finally的作用。
    比如,我们打开文件,如果抛出错误的话,我们总希望关闭这个文件句柄。
func contents(of filePath:String) throws -> String{
    let file = open(filePath,O_RDWR)
    defer {
        close(file)
    }
    //...
}

关于defer,有两点需要注意
多个defer会按照逆序的方式执行。
当你的程序遇到严重错误,比如fatalError,或者强制解析nil,或者segfaults的时候,defer的代码块并不会执行。

你可能感兴趣的:(Swift基础-14(错误处理))