Swift - 错误处理(Error Handling)

错误处理(Error Handling)

错误类型

  • 语法错误(编译错误)
  • 逻辑错误
  • 运行时错误(可能会导致闪退,一般也叫做异常)
  • ......

自定义错误

  • Swift 中可以通过 Error 协议自定义运行时的错误信息
enum SomeError: Error {
    case illegalArg(String)
    case outOfBounds(Int, Int)
    case outOfMemory
}
  • 函数内部通过 throw 抛出自定义 Error,可能会抛出 Error 的函数必须加上 throws 声明
func divide(_ num1: Int, _ num2: Int) throws -> Int {
    if num2 == 0 {
        throw SomeError.illegalArg("0 不能作为除数")
    }
    return num1 / num2
}
  • 需要使用 try 调用可能会抛出 Error 的函数
var result = try divide(20, 10)

do - catch

  • 可以使用 do - catch 捕捉 Error
func test() {
    print(1)
    do {
        print(2)
        print(try divide(20, 0))
        print(3)
    } catch let SomeError.illegalArg(msg) {
        print("参数异常:", msg)
    } catch let SomeError.outOfBounds(size, index) {
        print("下标越界:", "size = \(size)", "index = \(index)")
    } catch SomeError.outOfMemory {
        print("内存溢出")
    } catch {
        print("其他错误")
    }
    print(4)
}

test()
//1
//2
//参数异常: 0 不能作为除数
//4
do {
    print(try divide(20, 0))
} catch let error {
    switch error {
    case let SomeError.illegalArg(msg):
        print("参数错误:", msg)
    default:
        print("其他错误")
    }
}
  • 抛出 Error 后,try 下一句直到作用域结束的代码都将停止运行

处理 Error

处理 Error 的两种方式:
① 通过 do - catch 捕捉 Error
② 不捕捉 Error,在当前函数增加 throws 声明,Error 将自动抛给上层函数
③ 如果最顶层函数(main 函数)依然没有捕捉 Error,那么程序将终止

func test() throws {
    print(1)
    print(try divide(20, 0))
    print(2)
}
try test()
//1
//Fatal error: Error raised at top level: practice.SomeError.illegalArg("0 不能作为除数"): file ...
func test2() throws {
    print(1)
    do {
        print(2)
        print(try divide(20, 0))
        print(3)
    } catch let error as SomeError {
        print(error)
    }
    print(4)
}
try test2()
//1
//2
//illegalArg("0 不能作为除数")
//4

try?、try!

  • 可以使用 try?try!调用可能会抛出 Error 的函数,这样就不用去处理 Error
func test3() {
    print(try? divide(20, 10))
    print(try? divide(20, 0))
    print(try! divide(20, 10))
}
test3()
//Optional(2)
//nil
//2
  • a、b 是等价的
var a = try? divide(20, 0)
var b: Int?
do {
    b = try divide(20, 0)
} catch {
    b = nil
}

rethrows

  • rethrows 表明:函数本身不会抛出错误,但调用闭包参数抛出错误,那么它会将错误向上抛
func exex(_ fn: (Int, Int) throws -> Int, _ num1: Int, _ num2: Int) rethrows {
    print(try fn(num1, num2))
}

try exex(divide, 20, 0)
// Fatal error: Error raised at top level: practice.SomeError.illegalArg("0 不能作为除数"): file ...

defer

  • defer 语句:用来定义以任何方式(抛错误、return 等)离开代码块前必须要执行的代码
  • defer 语句将延迟至当前作用域结束之前执行
func open(_ filename: String) -> Int {
    print("open")
    return 0
}

func close(_ file: Int) {
    print("close")
}

func processFile(_ filename: String) throws  {
    let file = open(filename)
    defer {
        close(file)
    }
    // 使用 file
    // ...
    try divide(20, 0)
    
    // close 将会在这里调用
}

try processFile("book.txt")

//open
//close
//Fatal error: Error raised at top level: practice.SomeError.illegalArg("0 不能作为除数"): file ...

assert(断言)

  • 很多编程语言都有断言机制:不符合指定条件就抛出运行时错误,常用于调试(Debug)阶段的条件判断
  • 默认情况下,Swift 的断言只会在 Debug 模式下生效,Release 模式下忽略
func divide(_ v1: Int, _ v2: Int) -> Int {
    assert(v2 != 0, "除数不能为 0")
    return v1 / v2
}

print(divide(20, 0))
// Assertion failed: 除数不能为 0: file 
  • 增加 Swift Flags 修改断言的默认行为
  • -assert-config Release:强制关闭断言
  • -assert-config Debug:强制开启断言
Swift Flags

fatalError

  • 如果遇到严重问题,希望结束程序运行时,可以直接使用 fatalError 函数抛出错误(这是无法通过 do-catch 捕捉的错误)
  • 使用了 fatalError 函数,就不需要再写 return
func test(_ num: Int) -> Int {
    if num >= 0 {
        return 1
    }
    fatalError("num 不能小于 0")
}
  • 在某些不得不实现、但不希望别人调用的方法,可以考虑内部使用 fatalError 函数
class Person { required init() { } }
class Student: Person {
    required init() {
        fatalError("don't call Student.init")
    }
    init(score: Int) { }
}

var stu1 = Student(score: 99)
var stu2 = Student() // Fatal error: don't call Student.init

局部作用域

  • 可以使用 do 实现局部作用域
do {
    let dog = Dog()
    dog.age = 8
    dog.run()
}

你可能感兴趣的:(Swift - 错误处理(Error Handling))