swift抛出异常_Swift-错误处理

Swift-错误处理

关键字: throws、throw、 try、 try?、 try!、 do-catch、defer

错误处理 是指对代码中的异常情况, 作出响应的过程. swift 在运行时对错误的抛出、捕获、传递、操作提供了一级支持

开发过程中, 有些操作往往不能保证一定成功, 在失败时, 我们需要知道失败的原因, 因此, 便需要错误处理以便做出相应的响应.

例如: 从硬盘上读取文件时, 有多种原因会导致读取操作失败: 文件不存在、没有读取权限、文件格式不能正确编码等, 用不同的错误来区分这些状态, 可以让你的程序正确处理, 并能告诉用户失败的原因

对于错误表示, OC中用NSError, 而在swift中, 用Error

表示错误

在swift中, 使用遵守Error协议的类型来表示错误.

/// A type representing an error value that can be thrown.

public protocol Error {

}

Error 协议实际上是空的, 只是用来表示遵守该协议的某类型可以用于错误处理

注意: 抛出不遵守Error协议的类型时, 编译器会报错

枚举特别适用于封装错误, 可以并利用关联值特性, 来关联相关的错误信息, 如

enum VendingMachineError: Error {

case invalidSelection

case insufficientFunds(coinsNeeded: Int)

case outOfStock

}

抛出错误

使用关键字throw来执行抛出错误的操作, throw 后边的类型必须遵守Error协议, 否则报编译错误

抛出一个还需要5枚硬币的错误, 就可以这样:

throw VendingMachineError.insufficientFunds(coinsNeeded: 5)

处理错误

要表明一个函数、方法 或 构造器 可能会抛出错误, 可以使用关键字throws 声明在函数的形参之后, 则该函数即为throwing函数.

func canThrowErrors() throws -> String

func cannotThrowErrors() -> String

对于throwing函数, 调用时必须要处理错误, 否则报编译错误: Errors thrown from here are not handled

处理错误的方式有4种:

传递错误

捕获错误

转换错误为可选类型

断言错误

当函数抛出错误时, 会改变程序的当前执行流程, 因此在代码中确认抛出错误的位置显得尤为重要, 要确认抛出异常的位置,就需要使用关键字try, 或者try?, try!, 声明在函数调用前面.

注意:swift中的错误处理和其它语言的try catch throw类似, 但是与OC的异常处理不同, swift不会展开调用栈, 展开调用栈是一个很耗费性能的进程, 因此, throw 语句的性能与return语句差不多

传递错误:

throwing函数 会将在其内部抛出的错误传递到调用的代码块中, 如:

enum MyError: Error {

case error1, error2, error3

}

func throw1() throws {

throw MyError.error3

}

func throw2() throws {

do {

try throw1()

} catch MyError.error3 {

print("error3")

}

}

func excute() {

do {

try throw2()

} catch {

print(error)

}

}

excute()

throw1抛出的错误, 会传递到它的调用者throw2中, 如果throw2不能对该错误进行处理,则错误继续传递到throw2的调用者excute中, 在excute中进行处理

捕获错误

使用do-catch语句来捕获抛出的错误

如传递错误示例代码:

func excute() {

do {

try throw2()

doSomething()

} catch {

print(error)

}

}

do语句块中, 执行throwing函数throw2的调用

如果有错误抛出, 程序就会由do语句块转移到 catch语句, try throw2()之后的doSomething就不会执行了

如果没有错误, 则顺序执行do语句块之后的doSomething

注意: catch语句如果没有指定匹配的错误值, 默认会有一个本地的error变量

转换错误为可选类型

使用关键字try?来讲一个错误转换为可选类型

当有错误抛出时, 整个try? 表达式的值为nil, 如下, x 和 y的值相同

func someThrowingFunction() throws -> Int {

// ...

}

let x = try? someThrowingFunction()

let y: Int?

do {

y = try someThrowingFunction()

} catch {

y = nil

}

注意: 使用try?处理错误, 如果有返回值, 则不管返回值是什么类型, 都会加一层可选类型封装, 即Int变成Int?, Int?变成Int??

断言错误

使用关键字try!, 可以禁用错误传递, 因为系统会封装一层没有错误抛出的运行时断言, 如果有错误抛出, 则断言失败, 中断程序,报运行时错误

注意: 该方法适用于确定可以成功操作的情况, 使用时务必小心, 因为一旦判断不严谨, 会造成app崩溃

与Cocoa的桥接

Swift会将Cocoa中带error参数的方法, 转换为throwing函数

在Cocoa中, 表示错误, 通常使用NSError指针作为方法的最后一个参数, Swift会检查OC方法声明, 翻译为Swift的throwing函数, 函数名字甚至可以更短

例如, 移除文件的方法, 在OC中, 方法声明如下:

- (BOOL)removeItemAtURL:(NSURL *)URL

error:(NSError **)error;

而在swift中, 声明如下:

func removeItem(at: URL) throws

可以看到, swift中的函数是没有返回值的, 也没有error参数, 还多了throws声明.

具体转换规则如下:

如果OC方法的最后一个非block类型的参数, 是NSError**, swift就能将其转换为throwing函数

如果OC的error参数是它的第一个参数, swift会尝试移除WithError 或AndReturnError后缀 来简化方法名字

如果OC方法返回一个BOOL值来表明成功或失败,swift会改变返回值类型为Void

如果OC方法返回nil来表示方法调用失败, swift会改变返回值为nonoptional类型

如果转换方式, 推断不出来, 则默认保留方法名的左边部分

注意: 可以对OC方法添加宏NS_SWIFT_NOTHROW, 来表示阻止Swift转换为throwing函数

只能在OC中处理异常

在OC中, 异常与错误是明显不同的

异常: OC的异常处理使用@try、@catch、@throw语法来表明不可恢复的程序错误

错误: OC是用NSError来表示一个可以恢复的错误

在Swift中, 错误可以进行处理来恢复程序运行, 而对OC的异常, 则没有安全的方法来恢复, 所要处理OC中的异常, 只能用OC来处理后, 再在用Swift调用

延迟执行

使用关键字defer可以将一系列语句, 延迟到当前代码块结束时执行, 而不用关心具体在代码块中的位置, 一般用来做一些必要的清除操作.

func defer1() {

var c = 0

print(c)

c += 1

print(c)

defer {

c += 2

print("defer", c)

}

c += 1

print(c)

}

输出结果:

0

1

2

defer 4

可以看出: defer的执行时机,是在当前代码块的}之前, 且defer在执行前并不会捕获代码块中的变量

如果有多个defer, defer的执行会按照添加的反序执行, 即先添加的后执行, 如:

func deferN() {

var c = 0

print(c)

c += 1

print(c)

defer {

c += 2

print("defer1", c)

}

c += 1

print(c)

defer {

c += 3

print("defer2", c)

}

defer {

c += 5

print("defer3", c)

}

}

输出结果:

0

1

2

defer3 7

defer2 10

defer1 12

函数类型

defer执行时机

无返回值

在}之前执行

有返回值

在return之后, }之前执行

throwing

在throw之后, }之前执行

总结

表示错误: 使用遵守Error协议的类型, 通常多用枚举

传递错误: 使用throws关键字, 声明函数为throwing函数

抛出错误: 使用throw关键字, 来执行抛出错误的操作

转换错误: 使用try?关键字, 有错误,表达式则为nil,并对返回值进行可选类型封装

断言错误: 使用try!关键字, 有错误,则中断程序, 有危险, 慎用!

捕获错误: 使用try + do-catch关键字, 有错误, 程序跳转到catch; 无错误, 则继续执行do语句块

异常处理: 对于OC中的异常, 只能用OC语言来处理

延迟执行: 使用defer关键字, 延迟执行

难点: try?、try!、try, do-catch的区别

* try?: 尝试性的去做: 不管成功与否,都会正常执行下去, 不会打断程序执行流程, 有错误, 表达式就返回nil而已

* try!: 确定性的去做: 确定可以成功, 如果不成功, 则说明有问题, 报运行错误,程序终止

* try + do-catch: 负责任的去做:成功,则正常顺序执行;不成功, 对错误进行处理

* 注意: do-catch中也可以使用try?和try!但这样做毫无意义, 因为try?和try!实际上已经对错误进行了处理, 所以catch分支压根都不会被调用,也就相当于do{}, 只是加了一个内部作用域

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值