原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508

参考:http://wiki.jikexueyuan.com/project/swift/chapter2/18_Error_Handling.html

1、错误处理

错误处理是响应错误并从错误中返回的过程。swift提供一流错误支持,包括在运行时抛出,捕获,传送和控制可回收错误。

一些函数和方法不能总保证能够执行所有代码或产生有用的输出。可空类型用来表示值可能为空,但是当函数执行失败的时候,可空通常可以用来确定执行失败的原因,因此代码可以正确地响应失败。

举个例子,考虑到一个从磁盘上的一个文件读取以及处理数据的任务,有几种情况可能会导致这个任务失败,包括指定路径的文件不存在,文件不具有可读属性,或者文件没有被编码成合适的格式。区分这些错误可以让程序解决并且修复这些错误,并且,如果可能的话,把这些错误报告给用户。

注意:Swift中的错误处理涉及到错误处理样式,这会用到Cocoa中的NSError和Objective-C。

2、表示并且抛出错误

在Swift中,错误用符合ErrorType协议的值表示。

Swift枚举特别适合为一系列相关的错误建模,把一些表征错误本质的值关联在一起。

比如说,你可以这样表示操作自动贩卖机会出现的错误:

 enum VendingMachineError: ErrorType {
case InvalidSelection
case InsufficientFunds(coinsNeeded: Int)
case OutOfStock
}

在这种情况下,自动贩卖机可能会因为以下原因失败: 请求的物品不存在,用InvalidSelection表示。 请求的物品的价格高于已投入金额,用InsufficientFunds表示。相关的双精度值表示还需要多少钱来完成此次交易。 请求的物品已经卖完了,用OutOfStock表示。

错误抛出,可以让你知道异常发生并且正常的流程不能继续执行。通过在函数或方法声明的参数后面加上throws关键字,表明这个函数或方法可以抛出错误。例如自动贩卖机还需要5个硬币:

throw VendingMachineError.InsufficientFunds(coinsNeeded: )

3、处理错误

当错误被抛出时,周围的代码环境必须处理这个错误,例如纠正问题、尝试另一种方法或者将错误信息报告给用户。

Swift中有四种方式来处理错误。你可以将错误从函数中传递给调用这个函数的代码,又或者用do-catch语句来处理这个错误,又或者当作一个可选值来处理这个错误,或者断言这个错误不会发生。下面将一一介绍这四种方式。

当函数抛出错误后,它就会改变你的程序的流向,所以快速判断抛出错误的位置很重要。因此,在你调用可能抛出错误的函数、方法或者构造函数的前面,加上try关键字(或者try?、try!)。

注意:Swift的错误处理与其他语言类似,用 trycatch 和 throw等关键字。和很多语言(包括Objective-C)中的异常处理不一样,Swift中的处理不会展开调用堆(性能损耗很大的一个过程)。因此,throw语句的性能和return语句差不多。

(1)通过函数抛出错误

为了表示一个函数、方法或构造器可能抛出错误,在函数的参数后面添加throw关键字。

如果函数指定一个返回值,则把throws关键字放在返回箭头(->)的前面。

 func canThrowErrors() throws -> String

 func cannotThrowErrors() -> String

一个抛出函数(throwing function)在它被调用的地方传递它的函数体内抛出的错误。

注意:只有抛出函数能传递错误,非抛出函数内的错误必须被处理。

在下面的例子中,如果请求的物品不存在,或者卖完了,或者超出投入金额,vend(itemNamed:)函数会抛出一个错误:

 struct Item {
var price: Int
var count: Int
} class VendingMachine {
var inventory = [
"Candy Bar": Item(price: , count: ),
"Chips": Item(price: , count: ),
"Pretzels": Item(price: , count: )
]
var coinsDeposited =
func dispenseSnack(snack: String) {
print("Dispensing \(snack)")
} func vend(itemNamed name: String) throws {
guard var item = inventory[name] else {
throw VendingMachineError.InvalidSelection
} guard item.count > else {
throw VendingMachineError.OutOfStock
} guard item.price <= coinsDeposited else {
throw VendingMachineError.InsufficientFunds(coinsNeeded: item.price - coinsDeposited)
} coinsDeposited -= item.price
--item.count
inventory[name] = item
dispenseSnack(name)
}
}

首先,用guard语句来检测条件,如果条件不满足,则抛出错误并推出函数。因为throw语句会马上改变程序流程,当所有的购买条件(物品存在,库存足够以及投入金额足够)都满足的时候,物品才会出售。

当调用一个抛出函数的时候,在调用前面加上try。这个关键字表明函数可以抛出错误,而且在try后面代码将不会执行。

由于vend(itemNamed:)函数传递了抛出的错误,所以,你在代码中调用这个方法时,必须直接处理错误——用do-catch语句, try?,或者 try!,或者继续传递错误。

例如下例中的buyFavoriteSnack(_:vendingMachine:)函数也是一个抛出函数, vend(itemNamed:)函数抛出的错误将会在传递到buyFavoriteSnack(_:vendingMachine:)被调用的地方。

 let favoriteSnacks = [
"Alice": "Chips",
"Bob": "Licorice",
"Eve": "Pretzels",
]
func buyFavoriteSnack(person: String, vendingMachine: VendingMachine) throws {
let snackName = favoriteSnacks[person] ?? "Candy Bar"
try vendingMachine.vend(itemNamed: snackName)
}

buyFavoriteSnack(_:vendingMachine:)函数根据人名查询最喜爱的物品,并调用vend(itemNamed:)尝试购买。由于vend(itemNamed:)可能抛出一个错误,调用前加try关键字。

(2)用do-catch语句处理错误

可以用do-catch执行代码块来处理错误。如果do代码块中的代码抛出了错误,并且和catch子句中的错误列表匹配,那么这个错误就可以处理。

do-catch语句的格式一个catch分句包含一个catch关键字,跟着一个pattern来匹配错误和相应的执行语句。:

 do {
try expression
statements
} catch pattern {
statements
} catch pattern where condition {
statements
}

为了保证错误被处理,用一个带patterncatch分句来匹配错误。如果一个catch分句没有指定样式,这个分句会匹配并且绑定任何错误到一个本地error常量。

catch子句不必处理do子句中可能抛出的所有错误。如果一个错误没有被任意catch子句处理,这个错误将被传递到外部域。无论如何,这个错误必须在某个外部域中被处理,也许用一个封闭的do-catch子句处理,或者传递到一个抛出函数里。

 var vendingMachine = VendingMachine()
vendingMachine.coinsDeposited =
do {
try buyFavoriteSnack("Alice", vendingMachine: vendingMachine)
} catch VendingMachineError.InvalidSelection {
print("Invalid Selection.")
} catch VendingMachineError.OutOfStock {
print("Out of Stock.")
} catch VendingMachineError.InsufficientFunds(let coinsNeeded) {
print("Insufficient funds. Please insert an additional \(coinsNeeded) coins.")
}
// prints "Insufficient funds. Please insert an additional 2 coins."

在上面的例子中,vend(itemNamed:) 函数在try表达式中被调用,因为这个函数会抛出错误。如果抛出了错误,程序执行流程马上转到catch分句,在catch分句中确定错误传递是否继续传送。如果没有抛出错误,将会执行在do语句中剩余的语句。

(3)把错误转化成可选值

可以用try?来处理错误,这种方式把错误转化成可选值。如果一个错误在try?表达式中被抛出,这个表达式的值将为nil。

 func someThrowingFunction() throws -> Int {
// ...
} let x = try? someThrowingFunction() let y: Int?
do {
y = try someThrowingFunction()
} catch {
y = nil
}

如果someThrowingFunction()抛出错误,则x或y的值为nil。否则,x或y的值等于函数的返回值。注意,x和y的类型都是函数的返回类型的可选型。

当你在想用同一种方式处理所有的错误的时候,try?可以让你方便的写出精准的错误处理方法。

 func fetchData() -> Data? {
if let data = try? fetchDataFromDisk() { return data }
if let data = try? fetchDataFromServer() { return data }
return nil
}

(4)禁止错误传递

有时候,你清楚某个抛出函数实际上在运行时不会抛出错误。这种情况下,可以在要禁止错误传递的表达式前面加上try!关键字,并且把这个调用包装在一个断言里,判断是否没有任何错误抛出。如果抛出了错误,将产生一个运行时错误。

下面的例子,通过指定的路径加载图片资源,如果图片无法加载,则抛出错误。实际上,这个图片保存在应用程序的资源中,运行时不会产生错误,所以可以禁止错误的传递。

 let photo = try! loadImage("./Resources/John Appleseed.jpg")

(5)指定收尾操作

在当前的代码块运行结束前,用defer语句执行一系列语句。无论代码块是如何结束的(无论是抛出错误,还是return、break),defer语句都可以执行一些必要的动作。例如,可以用defer语句来保证文件描述符被关闭并且手动释放了内存。

defer语句把执行推迟到要退出当前域的时候。defer语句包括defer关键字以及后面要执行的语句。被推迟的语句可能不包含任何将执行流程转移到外部的代码,比如break或者return语句,或者通过抛出一个错误。被推迟的操作的执行顺序和他们定义的顺序相反,也就是说,在第一个defer语句中的代码在第二个defer语句中的代码之后执行。

 func processFile(filename: String) throws {
if exists(filename) {
let file = open(filename)
defer {
close(file)
}
while let line = try file.readline() {
// Work with the file.
}
// close(file) is called here, at the end of the scope.
}
}

上面的例子用defer语句来确保open(_:)函数一定调用对应的close(_:)函数。

注意:即使没有错误处理的代码,你也可以使用defer语句。

Swift2.1 语法指南——错误处理的更多相关文章

  1. Swift2.1 语法指南——访问控制

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  2. Swift2.1 语法指南——泛型

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  3. Swift2.1 语法指南——协议

    原档: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programm ...

  4. Swift2.1 语法指南——高级操作符

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  5. Swift2.1 语法指南——扩展

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  6. Swift2.1 语法指南——嵌套类型

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  7. Swift2.1 语法指南——类型转换

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  8. Swift2.1 语法指南——可空链式调用

    原档:https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programmi ...

  9. Swift2.1 语法指南——自动引用计数

    原档: https://developer.apple.com/library/prerelease/ios/documentation/Swift/Conceptual/Swift_Programm ...

随机推荐

  1. codevs 1063 合并果子//优先队列

    1063 合并果子 2004年NOIP全国联赛普及组  时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石    题目描述 Description 在一个果园里,多多已经将所有的果 ...

  2. ( 译、持续更新 ) JavaScript 上分小技巧(二)

    考虑到文章过长,不便于阅读,这里分出第二篇,如有后续,每15个知识点分为一篇... 第一篇地址:( 译.持续更新 ) JavaScript 上分小技巧(一) 第三篇地址:( 译.持续更新 ) Java ...

  3. Beta版本——第五次冲刺博客

    我说的都队 031402304 陈燊 031402342 许玲玲 031402337 胡心颖 03140241 王婷婷 031402203 陈齐民 031402209 黄伟炜 031402233 郑扬 ...

  4. Beta版本——第二次冲刺博客

    我说的都队 031402304 陈燊 031402342 许玲玲 031402337 胡心颖 03140241 王婷婷 031402203 陈齐民 031402209 黄伟炜 031402233 郑扬 ...

  5. primefaces p:tableData 显示 List<List>

    @javax.faces.bean.ViewScoped public class Controlador { private List<List> estadistico; @PostC ...

  6. linux下memcache的运用,和php结合小案例。

    由于是采用脚本安装的memache,所以软件的依赖关系我就不操心了,脚本已经帮我装好了和php的关联关系,实在是很省心.后续如果有需要,我会针对windows和linux各写一个安装和配置的说明,一来 ...

  7. JS性能方面--内存管理及ECMAScript5 Object的新属性方法

    Delete一个Object的属性会让此对象变慢(多耗费15倍的内存) var o = { x: 'y' }; delete o.x; //此时o会成一个慢对象 o.x; // var o = { x ...

  8. HTML5学习总结-11 IOS 控件WebView显示网页

    一 加载外部网页 1.使用UIWebView加载网页 运行XCode  新建一个Single View Application . 2 添加安全消息 添加以下消息到项目的  Info.plist &l ...

  9. python 培训之 Python 数据类型

    0. 变量 计算机某块内存的标签,存储数据的容器的标签,可被覆盖. a = ""     a = "a1bcd"       a=a+"ddd&quo ...

  10. C#读写文本文件

    static public string Read(string path) { StreamReader sr = new StreamReader(path,Encoding.Default); ...