语法错误:就平时写程序的时候会报的错误,在编译之前就得处理掉
逻辑错误:代码写错了,比如加写成-了
程序员日常解决最多的错误应该就是运行时错误了,跑着跑着不知道为毛挂了,然后就开始打断点做各种调试
func divide(dividend:Int,divisor:Int) -> Int { return dividend / divisor } print(divide(dividend: 20, divisor: 0))
在非自定义错误的情况下可以做如下处理
func divide(dividend:Int,divisor:Int) -> Int? { if divisor == 0 { return nil } return dividend / divisor } print(divide(dividend: 20, divisor: 0))
比如返回一个可选类型,处理在0的时候返回nil,这样后面判断一下这个可选类型就好了
捕获到错误了之后的自定义错误
struct MyError: Error { var errorCode:String } func divide(dividend:Int,divisor:Int) throws -> Int? { if divisor == 0{ throw MyError(errorCode: "除数不能是0") } return dividend / divisor } var result = try divide(dividend: 10, divisor: 0) print(result)
比如这里面就是一个自定义的错误,自定义错误是一个协议,所以枚举 结构体 类都可以继承,一般来说为了省空间可以考虑枚举,因为枚举实际占用字节是1个字节,如果是要报错的字符串,也最好用结构体,实际占用8个字节,如果用类的话实际占用就大很多了,指针是8个字节,里面是类信息最起码就16个字节,元类信息占用空间更大,所以能不用类尽量别用
上面的代码运行会报错,原因就是没有处理这个异常,Swift处理异常就是do-Catch语法
struct MyError: Error { var errorCode:String } func divide(dividend:Int,divisor:Int) throws -> Int? { if divisor == 0{ throw MyError(errorCode: "除数不能是0") } return dividend / divisor } func fn1() { do { try divide(dividend: 20, divisor: 0) } catch let myError as MyError { print(myError.errorCode) } catch { print("catch到未知错误") } } fn1()
enum MyError: Error { case errorCode(String) case outOfBounds(Int,Int) } func divide(dividend:Int,divisor:Int) throws -> Int? { if divisor == 0{ throw MyError.errorCode("除数不能是0") } return dividend / divisor } func fn1() throws { try divide(dividend: 20, divisor: 0) } func fn2() throws { try fn1() } do { try fn2() } catch let MyError.errorCode(errorCode) { print(errorCode) }catch let MyError.outOfBounds(index,limit) { print("index:\(index) out of \(limit)") }
假如在下层函数不用处理,也可以把错误往上抛,直到主函数,不过主函数不处理就会报错了
func divide(dividend:Int,divisor:Int) throws -> Int? { if divisor == 0{ throw MyError.errorCode("除数不能是0") } return dividend / divisor } let result = try? divide(dividend: 10, divisor: 0) print(result)
try?和try!的区别只在使用上面,本质就是把错误转为可选项,如果有错误就返回nil
enum MyError: Error { case errorCode(String) case outOfBounds(Int,Int) } func divide(dividend:Int,divisor:Int) throws -> Int { if divisor == 0{ throw MyError.errorCode("除数不能是0") } return dividend / divisor } func fn1(fn:(Int,Int) throws -> Int, a:Int , b:Int) rethrows { print(try fn(a,b)) } do { try fn1(fn: divide, a: 10, b: 0) } catch let MyError.errorCode(errorCode) { print(errorCode) }
当如果传入的闭包函数有问题,这个类也不准备处理的时候就用rethrows把错误传到更上一级的函数
struct LengthError:Error { var des:String } func open(path:String) -> Int { print("打开") return 0 } func read(length:Int,sign:Int) throws -> String { if(length > 1000) { throw LengthError(des:"太长了") } return "content" } func close() { print("关闭") } func readBook() throws { let sign = open(path: "123") try read(length: 1001, sign: sign) close() } try? readBook()
假如这是个读取代码,因为长度超过了1000函数不支持,抛出异常,这种情况来说,close()就不会调用了,因为异常之后的代码就不会执行了
func readBook() throws { let sign = open(path: "123") defer { close() } try read(length: 1001, sign: sign) }
假如加上了defer就会在该作用域结束前调用,比如这个就是在readBook结束之前调用,所以即使抛出异常也会执行关闭
注意点:
func readBook() throws { let sign = open(path: "123") defer{ print("1") } defer{ print("2") } defer{ print("3") } try read(length: 1001, sign: sign) }
同一个defer是从上往下,但不同的defer先声明的执行越晚,比如上面来说结果是3,2,1