Swift快速上手

【《Pro Swift》阅读笔记】06 - 错误处理

2018-01-17  本文已影响93人  Lebron_James

书籍链接:《Pro Swift》 (链接需要梯子才能打得开)。

一、rethrows的使用

我们先来看看Swift文档里的一句话:不会抛出错误的方法,属于会抛出错误的方法的一种。我们举个例子:

func definitelyWontThrow() {
   print("Shiny!")
}

try definitelyWontThrow()

definitelyWontThrow()这个方法不会抛出错误,但是我们也可使用try去调用,虽然Xcode会有警告。这也证明了Swift文档的那句话。

下面我们通过例子来说明rethrows的使用:假设我们有一个应用要获取用户的数据,可从服务器或者本地获取。我们定义一个Failure枚举来列出可能的错误,并写两个方法分别获取服务器和本地的数据:

enum Failure: Error {
    case badNetwork(message: String)
    case broken
}

func fetchRemote() throws -> String {
    // 从服务器获取数据,可能会有错误
    throw Failure.badNetwork(message: "Firewall blocked port.")
}

func fetchLocal() -> String {
    // 本地获取,不会抛出错误
    return "Taylor"
}

然后我们再写一个方法来统一获取用户的数据,可以把fetchRemote()fetchLocal()作为closure参数传进去:

func fetchUserData(using closure: () throws -> String) {
    do {
        let userData = try closure()
        print("User data received: \(userData)")
    } catch Failure.badNetwork(let message) {
        print(message)
    } catch {
        print("Fetch error")
    }
}

虽然这个方法要求传入的参数是会抛出错误的closure,但是我们之前说过:不会抛出错误的方法,属于会抛出错误的方法的一种,所以把不会抛出错误的fetchLocal()作为参数传入fetchUserData(using closure: () throws -> String)也是没有问题的。我们就可以这样使用:

fetchUserData(using: fetchLocal)

// 或者

fetchUserData(using: fetchRemote)

如果我们不想在fetchUserData(using closure: () throws -> String)方法里面处理错误,而是继续抛出错误,让它的使用者去处理,可以在方法后面加上throws关键字:

func fetchUserData(using closure: () throws -> String) throws {
   let userData = try closure()
   print("User data received: \(userData)")
}

在使用的时候就要处理错误:

do {
   try fetchUserData(using: fetchLocal)
} catch Failure.badNetwork(let message) {
   print(message)
} catch {
   print("Fetch error")
}

现在问题来了!!!当我传入fetchLocal()这个不会抛出错误的方法,根本就没有必要使用try/catch。这时我们可以使用rethrows来解决这个问题:

func fetchUserData(using closure: () throws -> String) rethrows {
   let userData = try closure()
   print("User data received: \(userData)")
}

这时我们如果我们传入fetchLocal(),根本不需要处理抛出错误的问题:

fetchUserData(using: fetchLocal)

如果是传入fetchRemote(),需要处理抛出的错误:

do {
   try fetchUserData(using: fetchRemote)
} catch Failure.badNetwork(let message) {
   print(message)
} catch {
   print("Fetch error")
}

二、try vs try? vs try!

处理Swift的抛出错误,有三种方式:

我们该如何选择?1. 如果我们关心抛出了什么错误,使用try;2. 如果我们不关心抛出的错误,并且不确定是否会抛出错误,使用try?;3. 如果我们不关心抛出的错误,并且确定不会抛出错误,或者如果抛出了错误就让应用crash,使用try!

三、断言

当我们在编写复杂应用时,断言是很有好处的。可以让程序满足某些条件时,才继续向下执行。例如:

let success = runImportantOperation()
assert(success == true, "Important operation failed!")

我们先来看看assert方法的定义:

public func assert(_ condition: @autoclosure () -> Bool,
                   _ message: @autoclosure () -> String = String(),
                   file: StaticString = #file,
                   line: UInt = #line) {

    if _isDebugAssertConfiguration() {
        if !_branchHint(condition(), expected: true) {
            _assertionFailed("assertion failed", message(), file,
                             line, flags: _fatalErrorFlags())
        } }
}

后面的两个参数fileline,Swift已经提供了默认值,分别代表当前文件和触发断言的行数。前面的conditionmessage参数都用了@autoclosure,这意味着这两个closure不会马上调用。我们从方法的实现可以看到,_isDebugAssertConfiguration()只有在debug模式下,才会运行里面的代码;然后!_branchHint(condition(), expected: true)再运行我们传入的condition,如果结果不是true,运行_assertionFailed

四、NeverfatalError()

Never是一个很特殊的返回值类型,意思是这个方法永远不会返回;不同于VoidVoid意思是返回空的东西。

fatalError()会马上终止应用,它的返回值类型正是NeverfatalError()可以替代无意义的返回,例如,我们在使用UITableView时,在cellForRowAt方法里面,首先会从队列里面取出已有的cell,得到一个UITableViewCell,然后在把它转成自定义的cell(MyCustomTableViewCell),最后再把cell返回。假如转型失败呢?通常我们是直接返回UITableViewCell()。但是理论上来说,如果转型失败的话,意味着代码出现了很严重的问题,我们返回UITableViewCell()也是无意义的,所以我们也可以直接在转型失败的时候调用fatalError(),不返回任何东西。

有任何问题,欢迎大家留言!

欢迎加入我管理的Swift开发群:536353151,本群只讨论Swift相关内容。

原创文章,转载请注明出处。谢谢!

上一篇 下一篇

猜你喜欢

热点阅读