JHHK

欢迎来到我的个人网站
行者常至 为者常成

12_01、error处理

目录

错误分析

一、使用方式

如果函数内有throw,函数声明必须使用throws关键字声明,也就是说 thorw 和 throws 必须同时使用。

    func readFile(at path: String) throws -> String {
        guard path != "" else {
            throw FileError.fileNotFound
        }
        // 假设成功读取文件
        return "File content"
    }  

调用有三种方式

1、使用try的时候,要么使用do catch捕捉错误,要么继续向上抛

// 捕获
func test(){
    do {
        let content = try readFile(at: "")
        print(content)
    } catch FileError.fileNotFound {
        print("File not found!")
    } catch FileError.insufficientPermissions {
        print("Insufficient permissions!")
    } catch {
        print("An unknown error occurred: \(error)")
    }
}


// 继续向上抛
func test() throws {
    try readFile(at: "")
}

2、使用try?的时候,如果函数内抛出错误,返回nil

let content1 = try? readFile(at: "")
print(content1 ?? "发生了错误")

3、try!: 断言不会抛出错误,如果出错则程序崩溃。

let content2 = try! readFile(at: "")
print(content1)

二、小码哥讲解

/// 一、错误分析
func errorExPlain()->Void {
    //Swift中可以通过Error协议自定义运行时的错误信息
    enum SomeError:Error{
        case illegalArg(String)
        case outOfBands(Int,Int)
        case outOfMemory
    }
    
    //函数内部通过throw抛出自定义Error,可能会抛出Error的函数必须加上throws声明
    func divide(_ num1:Int,_ num2:Int) throws->Int{
        if num2 == 0 {
            throw SomeError.illegalArg("0不能作为除数")
            //print("devide")//Code after 'throw' will never be executed
        }
        return num1/num2
    }
    
    
    //1、可以使用do-catch捕捉Error
    do{
        print("--------1---------")
        do {
            //需要使用try调用可能会抛出Error的函数
            try print(divide(10, 0))
        } catch  {
            //默认就有一个error存在
            print("抛出的error:",error)
        }
    }
    
    //2、catch let 绑定
    do{
        print("--------2---------")
        do {
            print("1")
            print(try divide(10, 0))
            //抛出Error后,try下一句直到作用域结束的代码都将停止运行
            print("2")
        } catch let SomeError.illegalArg(msg) {
            print("msg:",msg)
        } catch let SomeError.outOfBands(x,y){
            print("x is",x,"y is ",y)
        }catch SomeError.outOfMemory{
            print("内存溢出")
        }catch{
            print("other")
        }
        print("3")
    }
    
    
    //3、
    do{
        print("----------3---------")
        do {
            print(try divide(20, 0))
        } catch let error {
            switch error {
            case let SomeError.illegalArg(msg):
                print("参数错误:", msg)
            default:
                print("其他错误")
            }
        }
    }
   
    
    //4、
    do{
        print("----------4---------")

        do {
            print(try divide(20, 0))
        } catch is SomeError {
            print("SomeError:")
        }catch{
            print("other")
        }
    }
    
    
    //5、
    do{
        print("---------5----------")
        do {
            print("1")
            print(try divide(20, 0))
            print("2")
        } catch let error as SomeError {
            print("error:",error)
        }catch{
            print("other")
        }
        print("3")
    }
    
    
    //6、
    do{
        print("---------6----------")
         /**
         处理Error的2种方式
             通过do-catch捕捉Error
             不捕捉Error,在当前函数增加throws声明,Error将自动抛给上层函数
         
         如果最顶层函数(main函数)依然没有捕捉Error,那么程序将终止
         */
       
        func test () throws {
            print("1")
            print(try divide(20, 0))
            print("2")
        }
        
        func test2() throws {
            try test()
        }
        
        do {
            try test2()
        } catch let error {
            print("error:",error)
        }
    }
    
    
    //7、try?、try!
    do{
        print("---------7----------")
        //可以使用try?、try!调用可能会抛出Error的函数,这样就不用去处理Error
        
        print(try?divide(10, 1))
        print(try?divide(10, 0))
        
        print(try!divide(20, 1))
    //    print(try!divide(20, 0))//nil强制解包会报错

        //try?相当于下面的逻辑
        let b:Int?
        do {
            b = try divide(10, 0)
        } catch {
            b = nil
        }
        print(b)
    }
}

rethrows

一、使用方式

1、函数本身不会抛出错误,但闭包参数会抛出错误,闭包抛出的错误可以在函数内调用时进行捕获。

func execute(task: () throws -> Void) {
    // 需要捕捉错误
    do{
        try task()
    } catch FileError.fileNotFound {
        print("File not found!")
    } catch FileError.insufficientPermissions {
        print("Insufficient permissions!")
    } catch {
        print("An unknown error occurred: \(error)")
    }
}


// 调用
execute(task:{
    throw FileError.unknown
})

2、如果不在函数内进行捕获,可以将错误向上抛出,这时就可以不使用do…catch,而是需要在声明处使用rethrows关键字。
因为向上抛出了,所以调用外部函数时进行错误捕获。

func execute(task: () throws -> Void) rethrows {
    try task()
}


// 调用
do {
    try execute(task:{
        throw FileError.unknown
    })
} catch FileError.fileNotFound {
    print("File not found!")
} catch FileError.insufficientPermissions {
    print("Insufficient permissions!")
} catch {
    print("An unknown error occurred: \(error)")
}

rethrows跟throws的作用一样,只是rethrows表明错误是由闭包参数抛出的

二、小码哥讲解

/// 二、rethrows
func rethrowsUse(){
    
    struct MyError:Error{
        var msg:String
    }
    
    func divide(_ num1:Int,_ num2:Int) throws->Int{
        if num2 == 0 {
           throw MyError(msg: "除数不能为0")
        }
        return num1/num2
    }
    
    do {
        print(try divide(10, 0))
    } catch let error as MyError {
       print(error.msg)//除数不能为0
    }catch{
        print("other error")
    }
    
    
    //rethrows表明:函数本身不会抛出错误,但调用闭包参数抛出错误,那么它会将错误向上抛
    // rethrows 跟 throws的作用相同 只是表明这个错误不是函数本身抛出的
    func exec(_ fn:(Int,Int) throws->Int,_ num1:Int,_ num2:Int) rethrows->Int{
        return try fn(num1,num2)
    }
    
        
    do {
        print(try exec(divide, 10, 0))
    } catch let error {
        print(error)//MyError(msg: "除数不能为0")
    }
}

defer的用法

/// 三、defer的用法
func deferUse() -> Void {
    
    class MyError:Error{
        var msg:String
        var code:Int
        init(msg:String,code:Int) {
            self.msg = msg
            self.code = code
        }
    }
    
    func divide(_ num1:Int,_ num2:Int) throws->Int{
        if num2 == 0{
            throw MyError(msg:"除数不能为0", code:3)
        }
        return num1 / num2
    }
    
    do {
        print(try divide(10, 0))
    } catch let error as MyError {
        print(error.msg,error.code)
    }catch{
        print("other error")
    }
    
    print("-----------------")

    
    func open(_ fileName:String){
        print("打开\(fileName)")
    }
    
    func close(_ fileName:String){
        print("关闭\(fileName)")
    }
    
    
    func process(_ fileName:String) throws{
        open(fileName)
        
        
        //使用file过程中
        //...
        print(try divide(10, 0))
        
        //divide抛错之后会导致close无法执行 造成内存泄漏
        close(fileName)
    }
    
    
    func processFile(_ fileName:String) throws{
        open(fileName)
        
        // 在Swift中,defer语句用于在当前作用域退出之前执行一段代码。这无论是由于正常的返回还是由于异常抛出,defer中的代码都会被执行。
        // 这在资源管理、清理操作和确保某些操作总是执行时非常有用。
        defer{
            close(fileName)
        }

       
        
        //使用file过程中
        //...
        print("执行中...")
        print(try divide(10, 0))
        
    }
    
    do {
         print(try process("myFile"))
    } catch let error as MyError {
        print(error.msg,error.code)
    }catch{
        print("other error")
    }

    print("-----------------")
    
    do {
         print(try processFile("myFile"))
    } catch let error as MyError {
        print(error.msg,error.code)
    }catch{
        print("other error")
    }
    
    print("-----------------")

    
    //当有多个defer语句时,它们的执行顺序与定义顺序相反
    func fn1() { print("fn1") }
    func fn2() { print("fn2") }
    func test() {
        defer { fn1() }
        defer { fn2() }
    }
    test()

}

assert的使用

/// 四、assert的使用
func assertUse(){
    /**
     很多编程语言都有断言机制:不符合指定条件就抛出运行时错误,常用于调试(Debug)阶段的条件判断
     默认情况下,Swift的断言只会在Debug模式下生效,Release模式下会忽略

        增加Swift Flags修改断言的默认行为
        Other Swift Flags
            -assert-config Release:强制关闭断言
            -assert-config Debug:强制开启断言
     */
    
    func divide(v1:Int,v2:Int)->Int{
        //false触发断言
        assert(v2 != 0,"除数不能为0")
        return v1 / v2
    }
    
    print(divide(v1: 20, v2: 0))
}

fatalErro的使用

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

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.