Swift 错误处理

主要内容:

  • 如何使用do-catch
  • 如何表示错误
  • 如何使用availability属性

一.Swift 2.0之前的错误处理

错误处理是应用中响应错误与从错误中恢复的过程。在Swift 2.0之前,错误报告与Objective-C的模式相同,但是Swift可以使用可选类型的返回值,返回nil表示函数内部发生了错误。

最简单的错误处理,函数的返回值会表明函数执行是否成功:返回值可以是布尔值true/false,或者枚举enum,如果函数没有成功执行的话,该值可以表明哪里发生了错误。如果需要报告关于错误的额外信息,我们可以添加NSErrorPointer类型的NSError参数,但这不是最简单的途径,这些错误往往会被开发者忽略。下面的例子展示了Swift 2.0之前如何处理错误的:

var str = "Hello World"
var error: NSError

var results = str.writeToFile(path,atomically: true,encoding:NSUTF8StringEncoding,error: &error)

if results {
    //
} else {
    println("Error writing filer: \(error)")
}

尽管可以以这种方式处理错误,能够调整来满足大多数的需求,但是它一定不是最好的方案。这种方案有一些问题,最大的问题是开发者容易忽略函数返回值和错误自身。大多数有经验的开发者对于检查错误很小心,但是初学者很难理解,特别是函数没有包含NSError参数时。

除了使用NSError,我们也可以使用NSException类来抛出与捕获异常。但是,很少的开发者使用这种方法,甚至在Cocoa与Cocoa Touch框架中,这种处理异常的方法也很少被使用。

尽管使用NSError类与返回值来处理错误奏效,但是很多人,对于Apple刚开始发布Swift时没有包含额外的错误处理很失望。现在,Swift 2.0,我们拥有原生的错误处理。

二.Swift 2中的错误处理

《一.表示错误

在理解Swift中错误处理的工作原理前,必须要明白如何表示错误。在Swift中,错误是由遵守ErrorType协议的类型的值表示的。枚举非常适合作为错误情况的模型,因为通常我们有有限数量的错误情况来表示。

我们来看一下如何使用枚举来表示错误,首先,定义一个名字为MyError的错误,该错误有三种错误情况:MinorBadTerrible

enum MyError: ErrorType {
    case Minor
    case Bad
    case Terrible
}

在这个例子中,定义了遵守ErrorType协议的MyError枚举,然后定义了三中错误情况。我们也可以对错误情况使用相关值associated values。为其中一个错误情况添加描述:

enum MyError: ErrorType {
    case Minor
    case Bad
    case Terrible (description: String)
}

三.捕获错误

当函数抛出错误时,我们需要在调用该函数的代码中捕获它。使用do-catch代码块来完成,语法如下:

do {
    try 调用会抛出错误的函数
    ...
} catch [pattern] {
    ...
}

如果错误被抛出,它会一直往外传递,直到被catch处理。其中,catch后面跟上匹配错误的模式(a pattern to match the error)

import Cocoa

let maxNumber = 100
let minNumber = 8

enum PlayerNumberError: ErrorType {
    case NumberTooHigh(description: String)
    case NumberTooLow(description: String)
    case NumberAlreadyAssigned
    case NumberDoesNotExist
}

typealias BaseballPlayer = (firstName: String,lastName: String,number: Int)

struct BaseballTeam {
    var players: [Int: BaseballPlayer] = [ : ]

    mutating func addPlayer(player: BaseballPlayer) throws {
        guard player.number < maxNumber else {
            throw PlayerNumberError.NumberTooHigh(description: "Max number is \(maxNumber)")
        }
        guard player.number > minNumber else {
            throw PlayerNumberError.NumberTooLow(description: "Min number is \(minNumber)")
        }
        guard players[player.number] == nil else {
            throw PlayerNumberError.NumberAlreadyAssigned
        }
        players[player.number] = player
    }
    func getPlayerByNumber(number: Int) throws -> BaseballPlayer {
        if let player = players[number] {
            return player
        } else {
            throw PlayerNumberError.NumberDoesNotExist
        }
    }
}

var myTeam = BaseballTeam(players: [ : ])

do {
    let player = try myTeam.getPlayerByNumber(8)
    print("Player is \(player.firstName) \(player.lastName)")
} catch PlayerNumberError.NumberDoesNotExist {
    print("No player has that number")
}

上述例子中,do-catch块中调用了getPlayerByNumber()方法,如果队中没有成员分配该数字,则该方法会抛出PlayerNumberError.NumberDoesNotExist错误情况。因此,我们在catch表达式中匹配那个错误。

catch后面不必包含一个模式,如果catch后面没有包含一个模式,或者放置一个下划线,catch表达式会匹配所有的错误情况。例如,下面任何一个catch表达式都会捕获所有的错误:

do {

} catch {

}
do {

} catch _ {

}

如果想要获取错误,可以使用let关键字,如下:

do {

} catch let error {

}

使用catch表达式来捕获不同的错误情况:

do {
    try myTeam.addPlayer(("David","Ortiz",10))
} catch PlayerNumberError.NumberTooHigh(let description) {
    print("Error: \(description)")
} catch PlayerNumberError.NumberTooLow(let description) {
    print("Error: \(description)")
} catch PlayerNumberError.NumberAlreadyAssigned {
    print("Error: Number already assigned")
}

我们也可以让错误传递出去,而不是立即捕获它们。为了实现这样的目的,只需要在函数定义中添加throws关键字。例如下面的例子中,将错误传递到调用该函数的代码中,而不是在函数中处理错误。

func myFunc() throws {
    try myTeam.addPlayer(("David","Ortiz",34))
}

如果我们确定错误不会被抛出,可以使用强制try(forced-try)表达式来调用函数,写法是try!。强制try表达式阻止错误传递,会将函数调用包装在运行时断言中,这样调用就不会抛出错误。如果抛出了错误,就会得到运行时错误,所以使用这种表达式时要注意。

try?关键字尝试执行可能会抛出错误的操作。如果操作成功,会返回可选类型的结果。但是如果操作失败,抛出错误,会返回nil,错误会被丢弃。

因为try?关键字返回的结果是可选类型,通常将其与可选类型绑定结合,如下:

if let player = try? myTeam.getPlayerByNumber(34) {
    print("Player is \(player.firstName)")
}

如果需要执行一些清理工作,而不管是否有一些错误,可以使用defer。使用defer来在代码执行刚离开当前作用域前执行代码块,如下:

func deferFunction() {
    print("Function started")
    var str: String?

    defer {
        print("In defer block")
        if let s = str {
            print("str is \(s)")
        }
    }

    str = "John"
    print("Function finished")
}

如果调用这个函数,打印到控制台的第一行是Function started,代码的执行会跳过defer块,接着Function finished会打印到控制台。最后,离开函数域前,defer块会执行。执行的结果如下:

Function started
Function finished
In defer block
str is John

availability属性

Swift使用availability属性来安全地包装代码,只有在正确版本的操作系统可获得时,该代码才会运行。有两种方式来使用availability属性,第一种方式允许我们执行特定的代码块,与ifguard一同工作。第二种方式允许我们将一个方法或类型标记为只在特定平台上可获得。

availability属性可接受五个用逗号隔开的参数,允许我们定义执行代码所需要的操作系统与应用扩展的最低版本。这些参数包括:

  • iOS:兼容代码的最低iOS版本
  • OSX:兼容代码的最低OS X版本
  • watchOS:兼容代码的最低watchOS版本
  • iOSApplicationExtension:兼容代码的最低iOS应用kuoz
  • OSXApplicationExtension:兼容代码的最低OS X应用扩展

使用*来结束参数列表,我们来看一下如何只有在满足最小要求时才执行特定的代码块:

if #available(iOS 9.0,OSX 10.10,watchOS 2 ,*) {
    print("Minimum requirements met")
} else {
    print("Minimum requirements not met")
}

我们也可以限制对函数或类型的获取,之前availability的前缀是#,为了限制对函数或类型的获取,使用@作为前缀。

@available(iOS 9.0,*)
func testAvailability() {

}

@available(iOS 9.0,*)
struct TestStruct {

}

为了使用@available属性来限制对函数与类型的获取,必须要使用#available将调用该函数与类型的代码进行包装。

if #available(iOS 9.0,*) {
    testAvailability()
} else {

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值