美文网首页Swift快速上手
【《Pro Swift》阅读笔记】06 - 错误处理

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

作者: Lebron_James | 来源:发表于2018-01-17 23:25 被阅读93次

    书籍链接:《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的抛出错误,有三种方式:

    • try: 必须与catch配合使用
    • try?: 如果调用的方法抛出了错误,那么会自动返回nil;如果没有错误,那么返回带有值的可选类型
    • try!: 如果调用的方法抛出错误的话,应用会crash;如果没有错误,那么返回对应的值。

    我们该如何选择?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相关内容。

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

    相关文章

      网友评论

        本文标题:【《Pro Swift》阅读笔记】06 - 错误处理

        本文链接:https://www.haomeiwen.com/subject/doypoxtx.html