iOS - Swift 异常处理
版权声明:
本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《
阿里云开发者社区用户服务协议》和
《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写
侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。
简介:
前言在 Swift 1.0 时代是没有异常处理和抛出机制的,如果要处理异常,要么使用 if else 语句或 switch 语句判断处理,要么使用闭包形式的回调函数处理,再要么就使用 NSError 处理。
前言
1、Guard
在 Haskell, Erlang 等语言中早已存在 guard,在这里有更多关于它的介绍。guard 语句和 if 语句有点类似,都是根据其关键字之后的表达式的布尔值决定下一步执行什么。但与 if 语句不同的是,guard 语句只会有一个代码块,不像 if 语句可以 if else 多个代码块。guard 必须强制有 else 语句。guard 中的 else 只能执行转换语句,像 return, break, continue 或者 throws,当然你也可以在这里返回一个函数或者方法。那么 guard 语句的作用到底是什么呢?顾名思义,就是守护。guard 语句判断其后的表达式布尔值为 false 时,才会执行之后代码块里的代码,如果为 true,则跳过整个 guard 语句,我们举 例来看看。
-
我们以今年高考为例,在进入考场时一般都会检查身份证和准考证,我们写这样一个方法:
func checkup(person:[String:String!]) {
// 检查身份证,如果身份证没带,则不能进入考场
guard let id = person["id"] else {
print("没有身份证,不能进入考场!")
return
}
// 检查准考证,如果准考证没带,则不能进入考场
guard let examNumber = person["examNumber"] else {
print("没有准考证,不能进入考场!")
return
}
// 身份证和准考证齐全,方可进入考场
print("您的身份证号为:\(id),准考证号为:\(examNumber)。请进入考场!")
}
checkup(["id": "123456"]) // 没有准考证,不能进入考场!
checkup(["examNumber": "654321"]) // 没有身份证,不能进入考场!
checkup(["id": "123456", "examNumber": "654321"]) // 您的身份证号为:123456,准考证号为:654321。请进入考场!
上述代码中的第一个 guard 语句用于检查身份证,如果检查到身份证没带,也就是表达式为 false 时,执行大括号里的代码,并返回。第二个 guard语 句则检查准考证。如果两证齐全,则执行最后一个打印语 句,上面的两个 guard 语句大括号内的代码都不会执行,因为他们表达式的布尔值都是 true。
这里值得注意的是,id 和 examNumber 可以在 guard 语句之外使用,也就是说当 guard 对其表达式进行验证后,id 和 examNumber 可在整个方法的作用域中使用,并且是解包后的。
-
我们再用 if else 语句写一个类似的方法:
func checkupUseIf(person: [String: String!]) {
if let id = person["id"], let examNumber = person["examNumber"] {
print("您的身份证号为:\(id),准考证号为:\(examNumber)。请进入考场!")
} else {
print("证件不齐全,不能进入考场!")
}
print("您的身份证号为:\(id),准考证号为:\(examNumber)") // 报异常
}
checkupUseIf(["id": "123456"]) // 证件不齐全,不能进入考场!
checkupUseIf(["examNumber": "654321"]) // 证件不齐全,不能进入考场!
checkupUseIf(["id": "123456", "examNumber": "654321"]) // 您的身份证号为:123456,准考证号为:654321。请进入考场!
我们可以看到用 if else 实现的方法显然不如 guard 实现的那么精准。而且 id 和 examNumber 的作用域只限在 if 的第一个大括号内,超出这个作用域编译就会报错。
通过上述两个小例子不难看出,guard 语句正如一个称职的守卫,层层把关,严防一切不允许发生的事,并且让代码具有更高的可读性,非常棒。
2、Defer
-
在一些语言中,有 try/finally 这样的控制语句,比如 Java。这种语句可以让我们在 finally 代码块中执行必须要执行的代码,不管之前怎样的兴风作浪。在 Swift 2.0 中,Apple 提供了 defer 关键字,让我们可以实现同样的效果。
func checkSomething() {
print("CheckPoint 1")
doSomething()
print("CheckPoint 4")
}
func doSomething() {
print("CheckPoint 2")
defer {
print("Clean up here")
}
print("CheckPoint 3")
}
// CheckPoint 1, CheckPoint 2, CheckPoint 3, Clean up here, CheckPoint 4
checkSomething()
-
上述示例可以看到,在打印出 “CheckPoint 2” 之后并没有打印出 “Clean up here”,而是 “CheckPoint 3”,这就是 defer 的作用,它对进行了 print("Clean up here") 延迟。我们再来看一个 I/O 的示例:
// 伪代码
func writeSomething() {
let file = OpenFile()
let ioStatus = fetchIOStatus()
guard ioStatus != "error" else {
return
}
file.write()
closeFile(file)
}
-
上述示例是一个 I/O 操作的伪代码,如果获取到的 ioStatus 正常,那么该方法没有问题,如果 ioStatus 取到的是 error,那么会被 guard 语句抓到执行 return 操作,这样的话 closeFile(file) 就永远都不会执行了,一个严重的 Bug 就这样产生了。下面我们看看如何用 defer 来解决这个问题:
// 伪代码
func writeSomething() {
let file = OpenFile()
defer {
closeFile(file)
}
let ioStatus = fetchIOStatus()
guard ioStatus != "error" else {
return
}
file.write()
}
我们将 closeFile(file) 放在 defer 代码块里,这样即使 ioStatus 为 error,在执行 return 前会先执行 defer 里的代码,这样就保证了不管发生什么,最后都会将文件关闭。
-
在你的代码块就要结束前。如果你使用了 defer。在其之中的代码就会运行。等于说,给了你最后的机会来进行一些处理。如果你熟悉 BDD 或者 TDD,那么你可以参考他们中的 aferAll 机制。
func myFunction() throws {
defer {
// No matter what happened I need do something
print("All done, clean up here")
}
guard let item = item else {
// need throws the error out
throw MyError.NotExist
}
guard item.count > maxNumber else {
// need throws the error out
throw MyError.OutOfRange
}
// do something with item
// ...
}
注意,如果你有多个 defer 语句,他们在执行的顺序会和栈一样,最后一个进,第一个出。