Swift|Swift 2.0之错误处理(下)

在上一篇文章中,我们学习了 Swift 2.0 错误处理的基本用法。
这篇文章将继续介绍 Swift 2.0 配合错误处理的两个新关键字,deferguard
defer defer 关键字可以用来包裹一段代码,这个代码块将会在当前作用域结束的时候被调用。这通常被用来对当前的代码进行一些清理工作,比如关闭打开的文件等。
可以在同一个作用域中指定多个 defer 代码块,在当前作用域结束时,它们会以相反的顺序被调用,即先定义的后执行,后定义的先执行。
例如下面的代码:

openFile() defer { // defer block 1 closeFile() }startPortListener(42) defer { // defer block 2 stopPortListener(42) }

这段代码在作用域结束的时候,第二个 defer 块将首先被调用,其次再调用第一个 defer 块。
guard 【Swift|Swift 2.0之错误处理(下)】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 来让 endReceiptdo 的作用域结束之前得到调用:
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) }

这时,程序就就可以正常运行了。
作为练手,可以将 purchaseBookslendBooks 两个方法定义中的 if 改成使用 guard,这个很简单,就留给大家自己去尝试了。
本篇的完整代码可以到我的github上进行下载。

    推荐阅读