错误处理是响应错误以及从错误中恢复的过程。swift提供了在运行对可恢复错误抛出、捕获、传送和操作的高级支持(错误处理又称之为异常处理)。
 某些操作并不能总是保证执行所有代码都可以执行或总会产生出有用的结果。可选类型用来表示值可能为空,但当执行失败的时候,通常要去了解此次失败是由什么引起的,代码就可以做出与之对应的反应。
 例如,对磁盘上的某个文件的读取操作,该任务会有多种可能失败的情形,包括指定路径下文件不存在,文件没有可读权限,或文件的编码格式不兼容。具体区分这些错误,可以让程序解决并处理对应的错误,解决不了的错误即可以报告给用于。

一、表示并抛出错误


 在swift中,错误要遵循ErrorType协议类型的值来表示。这个协议表示一种可以用做错误处理的类型。swift的枚举类型特别适合于创建一组相关错误,而枚举的关联值还可以提供额外的信息,表示错误情形的性质。

// 例如去健身房,下面是影响去健身房的情形
enum myError:ErrorType {
    case noPower    // 没体力
    case noTime      // 没时间
    case noMachine // 没器械
}

二、错误处理


 在swift中有4种错误处理方式: 把函数抛出的错误传递给调用此函数的代码、用do-catch语句处理错误、将错误作为可选类型处理、直接断言此错误根本不会发生。

  • 用throwing函数传递错误。用thows关键字来标识一个可抛出错误的函数、方法或构造器。在函数声明的参数列表之后加上throws,如果函数是有返回值的,throws关键字需要卸载箭头->的前面:
enum myError:ErrorType {
    case noPower    // 没体力
    case noTime      // 没时间
    case noMachine // 没器械
}

// 抛出异常(throwing函数)
func checkKeepFit(power:Int, time:Int, machine:Int) throws {
    // guard关键字,若守卫条件为不为真,即抛出异常。类似if语句,但不是if,guard只能用在异常处理中
    // throws 表示该函数可能抛出异常,放在返回值前面
    // throw  表示某个条件不符合的时候,抛出异常
    guard power > 0 else {
        throw MyError.noPower
    }
    guard time > 0 else {
        throw MyError.noTime
    }
    guard machine > 0 else {
        throw MyError.noMachine
    }
}

// 调用checkKeepFit函数(即在此函数中处理异常)
func goToKeepFit(power:Int, time:Int, machine:Int) {
    /**  异常的处理
    try!和try?: 用于标记一个被调用的抛出对象的方法,你等于告诉编译器这个错误永远不会发生,并且你也不需要捕获它。
    try!: 如果该语句本身产生错误(error),应用程序会自动停止执行,那么你就要开始调试了;
    try?: 如果该语句本身产生了错误(error),应用程序也不会停止执行;
    */
    try? checkKeepFit(power, time: time, machine: machine)
    
    // 如果上面使用try!,那么应用程序会报错
    print("---------")
}

// 函数调用
goToKeepFit(0, time: 1, machine: 1)
输出结果:
---------
  • do-catch处理错误。即用do-catch语句运行一段闭包代码来做错误处理,如果do语句中的代码抛出了一个错误,则这个错误会与catch语句做匹配来决定哪条语句处理:
// 调用checkKeepFit函数(即在此函数中处理异常)
func goToKeepFit(power:Int, time:Int, machine:Int) {
    // try 捕获异常
    do {
        try checkKeepFit(power, time: time, machine: machine);
    } catch MyError.noPower {
        print("没有体力");
    } catch MyError.noTime {
        print("没有时间");
    } catch MyError.noMachine {
        print("没有器材");
    } catch {   // 不能少这个,如果缺少,那么上面try会出错!!!
        print("未知异常");
    }
}

goToKeepFit(0, time: 1, machine: 1)

输出结果:
没有体力
  • 将错误转换成可选值。可以使用try?通过将其转换成一个可选值来处理错误,如果在评估try?表达式时一个错误被抛出,那么这个表达式就是nil
enum MyError:ErrorType {
    // 负数
    case negativeNumer
}

// 判断是否为正数
func isPlus(a:Int) throws -> Int {
    // 如果a大于0,则程序继续向下执行  
    // 否则,就会是抛出异常
    guard a > 0 else {  
        throw MyError.negativeNumer
    }
    
    return a
}

// 如果`isPlus`抛出异常,那么x值就是为`nil`
// 否则就是该函数的返回值
let x = try? isPlus(10)
// 注意: x自动推断是为可选类型
print(x!)

// 这里就是保证y肯定就是不为负数
let y:Int
do {
    y = try isPlus(-1)
} catch {
    y = 0
}
print(y)
输出结果:
10
0
  • 使错误传递失败。当知道某个throwing函数在实际运行中是不会抛出错误的,那么就可以在表达式前面添加try!来使错误传递失败,但实际上抛出了错误,就会得到运行时错误:
例如使用`loadImage(_:)`函数,该函数即是从给定路径中加载图片资源,如果图片不能被加载则会抛出一个错误。而图片和应用是绑定在一起的,应用程序运行时不会有错误被抛出,所以可以让错误传递失败
// 该图片地址是虚拟的
let photo = try! loadImage("./User/EndEvent/head.png")

三、指定清理操作


 可以使用defer语句在代码执行到要离开当前代码段之前,再去执行一段代码。该代码能够执行必要清理操作,不管是以何种方式离开当前的代码段(不论是抛出错误离开还是return或是break语句)。

// 文件处理
func processFile(fileName:String) throws {
    // 判断文件是否存在,存在才做具体操作
    if exists(fileName) {
        // 打开文件
        let file = open(fileName)
        defer { // 这就可以保证在文件描述符在退出后被关闭
            close(file)
        }
        
        while let line try file.readline() {
            // 对应处理文件
        }
        
        // 处理完成,将关闭文件描述符
        close(file)
    }
}

注意: 即是没有涉及到错误处理代码,也可以使用defer语句。

注:xcode7.3环境