小编典典

Swift 语言中的错误处理

all

我对 Swift 的了解并不多,但我注意到的一件事是没有例外。那么他们如何在 Swift 中进行错误处理呢?有没有人发现任何与错误处理有关的东西?


阅读 105

收藏
2022-07-16

共1个答案

小编典典

斯威夫特 2 & 3

Swift 2 中的情况发生了一些变化,因为有一个新的错误处理机制,它有点类似于异常,但在细节上有所不同。

1.指示错误可能性

如果函数/方法想要表明它可能会抛出错误,它应该包含throws这样的关键字

func summonDefaultDragon() throws -> Dragon

注意:函数实际上可以抛出的错误类型没有规范。 这个声明只是声明函数可以抛出任何实现 ErrorType 类型的实例,或者根本不抛出。

2.调用可能抛出错误的函数

为了调用函数,你需要使用 try 关键字,像这样

try summonDefaultDragon()

这条线通常应该像这样存在 do-catch 块

do {
    let dragon = try summonDefaultDragon() 
} catch DragonError.dragonIsMissing {
    // Some specific-case error-handling
} catch DragonError.notEnoughMana(let manaRequired) {
    // Other specific-case error-handlng
} catch {
    // Catch all error-handling
}

注意:catch 子句使用了 Swift 模式匹配的所有强大功能,因此您在这里非常灵活。

throws如果您从本身标有关键字的函数调用抛出函数,则您可能决定传播错误:

func fulfill(quest: Quest) throws {
    let dragon = try summonDefaultDragon()
    quest.ride(dragon)
}

或者,您可以使用以下方法调用 throwing 函数try?

let dragonOrNil = try? summonDefaultDragon()

这样,如果发生任何错误,您要么得到返回值,要么得到 nil。使用这种方式你不会得到错误对象。

这意味着您还可以结合try?有用的语句,例如:

if let dragon = try? summonDefaultDragon()

或者

guard let dragon = try? summonDefaultDragon() else { ... }

最后,您可以确定您知道错误实际上不会发生(例如,因为您已经检查了先决条件)并使用try!关键字:

let dragon = try! summonDefaultDragon()

如果函数确实抛出错误,那么您将在应用程序中收到运行时错误,并且应用程序将终止。

3. 抛出错误

为了抛出错误,您可以使用 throw 关键字这样

throw DragonError.dragonIsMissing

你可以抛出任何符合ErrorType协议的东西。对于初学者NSError,符合此协议,但您可能希望使用基于枚举的方法ErrorType,它使您能够对多个相关错误进行分组,可能还有额外的数据,像这样

enum DragonError: ErrorType {
    case dragonIsMissing
    case notEnoughMana(requiredMana: Int)
    ...
}

新的 Swift 2 和 3 错误机制与 Java/C#/C++ 样式异常的主要区别如下:

  • 语法有点不同:++ do-catchvs传统语法。try``defer``try-catch-finally
  • 异常处理在异常路径中的执行时间通常比在成功路径中要长得多。Swift 2.0 错误并非如此,成功路径和错误路径的成本大致相同。
  • 必须声明所有抛出错误的代码,而可能从任何地方抛出异常。在 Java 命名法中,所有错误都是“检查异常”。但是,与 Java 相比,您不指定可能引发的错误。
  • Swift 异常与 ObjC 异常不兼容。您的do-catch块不会捕获任何 NSException,反之亦然,因为您必须使用 ObjC。
  • Swift 异常与返回(用于返回函数)或(用于返回函数)并传递错误详细信息的 CocoaNSError方法约定兼容。false``Bool``nil``AnyObject``NSErrorPointer

作为简化错误处理的额外语法糖,还有两个概念

  • 延迟操作(使用defer关键字),它可以让您实现与 Java/C#/etc 中的 finally 块相同的效果
  • 保护语句(使用guard关键字),它让你编写的 if/else 代码比正常的错误检查/信号代码少一些。

斯威夫特 1

运行时错误:

正如 Leandros 建议的处理运行时错误(如网络连接问题、解析数据、打开文件等),您应该NSError像在 ObjC 中那样使用,因为
Foundation、AppKit、UIKit 等以这种方式报告它们的错误。所以它更多的是框架而不是语言。

另一个常用的模式是分隔成功/失败块,如 AFNetworking:

var sessionManager = AFHTTPSessionManager(baseURL: NSURL(string: "yavin4.yavin.planets"))
sessionManager.HEAD("/api/destoryDeathStar", parameters: xwingSquad,
    success: { (NSURLSessionDataTask) -> Void in
        println("Success")
    },
    failure:{ (NSURLSessionDataTask, NSError) -> Void in
        println("Failure")
    })

仍然是失败块经常收到NSError的实例,描述了错误。

程序员错误:

对于程序员错误(如数组元素的越界访问、传递给函数调用的无效参数等),您在 ObjC 中使用了异常。Swift
语言似乎没有任何语言支持异常(如throw, catch, 等关键字)。但是,正如文档所暗示的那样,它与 ObjC
在同一运行时运行,因此您仍然可以NSExceptions像这样抛出:

NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()

尽管您可以选择在 ObjC 代码中捕获异常,但您无法在纯 Swift 中捕获它们。

问题是您是否应该为程序员错误抛出异常,或者更确切地说使用 Apple 在语言指南中建议的断言。

2022-07-16