在上一篇文章中,我们学习了 Swift 2.0 错误处理的基本用法。
这篇文章将继续介绍 Swift 2.0 配合错误处理的两个新关键字,defer
与 guard
。
defer
defer
关键字可以用来包裹一段代码,这个代码块将会在当前作用域结束的时候被调用。这通常被用来对当前的代码进行一些清理工作,比如关闭打开的文件等。
可以在同一个作用域中指定多个 defer
代码块,在当前作用域结束时,它们会以相反的顺序被调用,即先定义的后执行,后定义的先执行。
例如下面的代码:
openFile()
defer {
// defer block 1
closeFile()
}
startPortListener(42)
defer {
// defer block 2
stopPortListener(42)
}
这段代码在作用域结束的时候,第二个 defer
块将首先被调用,其次再调用第一个 defer
块。
guard
guard
关键字与 if
很相似, 只是作用与它相反。我们通常使用 if
用来选择自己想要的条件,而 guard
则是用来选择不想要的条件。
比如,有个程序员俱乐部规定在 github 上的 star 数少于 200 的不得入内。如果用 if
来进行判断,代码是这样的:
if star < 200 {
return
}
如果使用 guard
的话,则应该是:
guard star >= 200 else {
return
}
对于可选类型的绑定来说,guard
关键字的好处更加明显。下面的代码对比了这两种方法:
var star: Int?
if let star = star {
// star 现在已经可以放心地使用了
}
guard let star = star else {
return
}
// 从这里到本作用域结束,star都可以放心使用了
从段代码中可以看到,如果使用普通的 if-let
来对可选类型进行绑定,则意味着,我们必须在 if
语句的大括号里处理正常的情况,而在外面处理错误的情况。这与我们的直观感觉(特别是强迫症患者)不符。但是如果使用 guard
的话,我们可以提前判断出空的可选值,然后提前退出,在错误判断之后的代码中,star
都是解包过的,可以放心使用了。
Show me the code
这里继续使用上一篇文章中的 Demo,上一章的完整代码可以从我的github上下载到,文件名为 01-ErrorHandling.playground
。
在starterShelf
函数之前,添加两个函数的定义:
func beginReceipt() {
print("又要开始大采买了,真是买书如山倒啊")
}
func endReceipt() {
print("购物结束,该去剁手去了")
}
接着,在调用purchaseBooks
方法的前后,调用刚刚定义的两个函数:
do {
beginReceipt()
try shelf.purchaseBooks(2500)
endReceipt()
} catch BookShelfError.NoEnoughSpace(let required) {
print("书柜满啦,新买的书放不下咯。还差\(required)个空位,赶紧买个新书柜吧")
}
可以看到, 只有前一个函数的信息被打印出来了,那是因为在执行 purchaseBooks
方法的时候抛出了错误,程序的控制流程被转到了 catch
代码块里面,第二个函数没有获得执行。
这里,就可以使用 defer
来让 endReceipt
在 do
的作用域结束之前得到调用:
do {
beginReceipt()
defer {
endReceipt()
}
try shelf.purchaseBooks(2500)
} catch BookShelfError.NoEnoughSpace(let required) {
print("书柜满啦,新买的书放不下咯。还差\(required)个空位,赶紧买个新书柜吧")
}
现在,假设我们使用了一个字典来保存要在哪个书店购买几本书,将下面代码添加在 BookShelfError
的定义下面:
let shopList = ["新华书店": 250]
在调用 purchaseBooks
的时候,先从 shopList
里面取出要购买的数量:
do {
// ...
let buyCount = shopList["新华书店"]
try shelf.purchaseBooks(buyCount)
}
如果直接这样调用的话,编译器会报错,因为字典类型返回的是一个 Int?
的可选类型,所以不能直接使用。
这里,guard
关键字就可以派上用场了:
do {
// ...
guard let buyCount = shopList["新华书店"] else {
fatalError()
}
try shelf.purchaseBooks(buyCount)
}
这时,程序就就可以正常运行了。
作为练手,可以将 purchaseBooks
和 lendBooks
两个方法定义中的 if
改成使用 guard
,这个很简单,就留给大家自己去尝试了。
本篇的完整代码可以到我的github上进行下载。