是否可以在Swift中抛出“RuntimeException”而不声明它?

时间:2015-11-29 00:17:39

标签: swift exception exception-handling runtimeexception

我想从一些“深度”函数中抛出一个异常,所以它会冒泡到另一个函数,我想抓住它。

f1拨打f2来电f3来电...... fN可能会引发错误

我想从f1收到错误。

我已经读过,在Swift中我必须使用throws声明所有方法,并使用try调用它们。

但那很烦人:

enum MyErrorType : ErrorType {
    case SomeError
}

func f1() {
    do {
        try f2()
    } catch {
        print("recovered")
    }
}

func f2() throws {
    try f3()
}

func f3() throws {
    try f4()
}

...

func fN() throws {
    if (someCondition) {
      throw MyErrorType.SomeError
    }
}

与Java中的RuntimeException是否存在类似的概念,其中throws不会在调用链中一直泄漏?

4 个答案:

答案 0 :(得分:30)

是的,有可能!

使用:fatalError("your message here")抛出运行时异常

答案 1 :(得分:6)

Swift中的错误处理机制不涉及提升未检查(运行时)异常。相反,需要显式错误处理。 Swift肯定不是最近为此设计设计的唯一语言 - 例如RustGo也以自己的方式也需要在代码中明确描述错误路径。在Objective-C中,存在未经检查的异常功能,但主要仅用于传递程序员错误,除了一些关键的Cocoa类(如NSFileHandle之外),这些类往往会让人们感到不舒服。

从技术上讲,您可以使用NSException(name: "SomeName", reason: "SomeReason", userInfo: nil).raise()来解释Swift中的Objective-C异常,如in this excellent answer this question所述,可以说是您的问题的副本。你真的不应该提出NSExceptions(尤其是因为你在Swift中没有可用的Objective-C异常捕获语言功能)。

他们为什么选择这种设计? Apple的"Error Handling in Swift 2.0"文档清楚地解释了基本原理。引自那里:

  

这种方法与手动错误处理模型非常相似   使用NSError约定在Objective-C中实现。值得注意的是,   方法保留了这个惯例的这些优点:

     
      
  • 方法是否产生错误(或不是)是其API合同的明确部分。
  •   
  • 方法默认不产生错误,除非明确标记。
  •   
  • 函数中的控制流仍然是显式的:维护者可以确切地确定哪些语句可以产生错误,并且   一个简单的检查揭示了该功能如何对错误作出反应。
  •   
  • 抛出错误提供了与分配错误并返回错误类似的性能 - 它不是一个昂贵的,基于表的堆栈   放卷过程。使用标准NSError模式的Cocoa API可以   自动导入这个世界。其他常见模式(例如   CFError,errno)可以在未来版本的Swift中添加到模型中。
  •   
     

[...]

     

至于基本语法,我们决定坚持使用熟悉的语言   异常处理。 [...]总的来说,错误传播在此   提案的工作方式与异常处理方式类似,人们也是如此   不可避免地要建立联系。

答案 2 :(得分:6)

要详细说明Максим Мартынов's answer,Swift有三种方法可以抛出未声明的,无法捕获的错误。这些基于3个优化级别:

  1. -Onone:没有优化; 调试构建
  2. -O:正常优化; 发布构建
  3. -O SWIFT_DISABLE_SAFETY_CHECKS:未经检查的优化; 极度优化构建
  4. 1。 assertionFailure(_:)

    在进行调试测试时写下这一行并点击你不认为应该被击中的一行。 这些将在非调试版本中删除,因此您必须假设它们永远不会在生产应用中被点击。

    这有一个名为assert(_:_:)的姐妹函数,它允许您在运行时断言条件是否为真。当你知道情况总是很糟糕的时候,assertionFailure(_:)就是你所写的,但不要认为这会严重损害生产代码。

    用法:

    if color.red > 0 {
        assertionFailure("The UI should have guaranteed the red level stays at 0")
        color = NSColor(red: 0, green: color.green, blue: color.blue)
    }
    

    2。 preconditionFailure(_:)

    如果您确定某些条件(在文档中等)未得到满足,请写下此行。 这与assertionFailure(_:)类似,但在发布版本和调试版本中都有效。

    assertionFailure(_:)一样,这个有一个名为precondition(_:_:)的姐妹函数,它允许您在运行时决定是否满足前提条件。 preconditionFailure(_:)本质上就是这个,但假设一旦程序到达该行,就永远不会满足前提条件。

    用法:

    guard index >= 0 else {
        preconditionFailure("You passed a negative number as an array index")
        return nil
    }
    

    请注意,在极其优化的版本中,未定义如果此行被点击会发生什么!因此,如果您不希望自己的应用在可能遇到此问题时退出,那么确保错误状态可以处理。

    3。 fatalError(_:)

    用作最后的手段。当其他每一次挽救这一天的尝试失败时,这就是你的核武器。打印传递给它的消息(连同文件和行号)后,程序停止运行。

    一旦程序到达此行,此行始终运行,程序永远不会继续。 即使在极其优化的版本中也是如此。

    用法:

    #if arch(arm) || arch(arm64)
        fatalError("This app cannot run on this processor")
    #endif
    

    进一步阅读:Swift Assertions by Andy Bargh

答案 3 :(得分:0)

  

Java中没有RuntimeException并没有在调用链中泄漏的概念吗?

Swift确实具有错误处理功能,在编译时不会出错。

但是,在讨论这些内容之前,我必须说的是,您指出的是使用该语言的throwsdo...catchtrythrow关键字的地方/ features处理错误,是迄今为止最安全,最优选的功能。这样可以确保每次抛出或捕获错误时,都可以正确处理。这完全消除了意外错误,使所有代码更加安全和可预测。由于具有固有的编译和运行时安全性,您应尽可能使用它。

throws
func loadPreferences() throws -> Data {
    return try Data(contentsOf: preferencesResourceUrl, options: [.mappedIfSafe, .uncached])
}


func start() {
    do {
        self.preferences = try loadPreferences()
    }
    catch {
        print("Failed to load preferences", error)
        assertionFailure()
    }
}

还有I described in detail in my answer from October of 2017guard let fileSizeInBytes = try? FileManager.default.attributesOfItem(atPath: path)[.size] as? Int64 else { assertionFailure("Couldn't get file size") return false } assertionprecondition s。编译器对此提供了合理的处理,例如确保在适当的情况下放置并省略return语句和其他控制流。

如果您的目标是立即停止该程序,则

fatalError属于这个家庭。


如果您在Swift之外涉足更广泛的生态系统,那么您还会看到Objective-C的NSException。如您所愿,Swift可以抛出此错误,而无需使用任何防止该行为的语言功能。确保记录下来!但是,这不能单独由Swift捕获!。您可以编写a thin Objective-C wrapper,让您在Swift世界中与之互动。

exit

当然,如果您是在Unix环境中编写Swift,您仍然可以访问Unix interrupts的恐怖世界。 You can use Grand Central Dispatch to both throw and catch these。而且,如您所愿,编译器无法防止它们被抛出。

func silentButDeadly() {
    // ... some operations ...

    guard !shouldThrow else {
        NSException.raise(NSExceptionName("Deadly and silent", format: "Could not handle %@", arguments: withVaList([problematicValue], {$0}))
        return
    }

    // ... some operations ...
}


func devilMayCare() {
    // ... some operations ...

    silentButDeadly()

    // ... some operations ...
}


func moreCautious() {
    do {
        try ObjC.catchException {
            devilMayCare()
        }
    }
    catch {
        print("An NSException was thrown:", error)
        assertionFailure()
    }
}
如果您的目标是trap it并阅读其代码,那么

import Dispatch // or Foundation signal(SIGINT, SIG_IGN) // // Make sure the signal does not terminate the application. let sigintSource = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main) sigintSource.setEventHandler { print("Got SIGINT") // ... exit(0) } sigintSource.resume() 就属于这个家庭。