15

我知道 Cocoa 中有一个UncaughtExceptionHandler,但是我正在为 Swift 寻找同样的东西。即,每当应用程序中的任何错误/异常由于任何错误而没有在本地捕获时,它应该一直冒泡到顶级应用程序对象,在那里我应该能够优雅地处理它并适当地响应用户。

安卓有。弹性有它。Java有它。想知道为什么 Swift 缺少这个关键特性。

4

2 回答 2

16

这是我用来记录所有异常/错误的代码。Log.error(with:)是一个自定义函数,我在其中存储堆栈跟踪以及其他信息。Thread.callStackSymbols是一个字符串数组,表示堆栈跟踪。

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? = nil) -> Bool {

    NSSetUncaughtExceptionHandler { exception in
        Log.error(with: Thread.callStackSymbols)
    }

    signal(SIGABRT) { _ in
        Log.error(with: Thread.callStackSymbols)
    }

    signal(SIGILL) { _ in
        Log.error(with: Thread.callStackSymbols)
    }

    signal(SIGSEGV) { _ in
        Log.error(with: Thread.callStackSymbols)
    }

    signal(SIGFPE) { _ in
        Log.error(with: Thread.callStackSymbols)
    }

    signal(SIGBUS) { _ in
        Log.error(with: Thread.callStackSymbols)
    }

    signal(SIGPIPE) { _ in
        Log.error(with: Thread.callStackSymbols)
    }

    return true
}
于 2017-04-04T08:47:23.310 回答
15

Swift 没有捕获所有任意运行时异常的机制。原因解释在

在 swift-users 论坛中。提炼:

Swift 有意识地选择不包含通过任意堆栈帧抛出的异常,不是因为这在技术上是不可能的,而是因为它的设计者认为成本太高。

问题是这样的:如果一段代码由于错误而提前退出,则必须编写它来处理该提前退出。否则它会行为不端——无法释放内存、无法关闭文件句柄/套接字/数据库连接/其他、无法释放锁等。在像 Java 这样的语言中,编写真正的异常安全代码需要大量的 try/finally块。这就是为什么没有人这样做。他们判断他们可能会看到哪些异常以及哪些资源有泄漏的危险,并且只保护他们的代码免受那些特定的预期条件的影响。然后发生了一些无法预料的事情,他们的程序中断了。

这在像 Swift 这样的引用计数语言中更糟糕,因为在存在异常的情况下正确平衡引用计数基本上需要每个函数都包含一个隐式 finally 块来平衡所有保留计数。这意味着编译器必须在某个调用或另一个调用引发异常的情况下生成大量额外代码。绝大多数代码从未使用过,但它必须存在,从而使过程膨胀。

由于这些问题,Swift 选择不支持传统的异常;相反,它只允许您在特殊标记的代码区域中抛出错误。但作为一个推论,这意味着,如果无法抛出的代码中出现了真正的错误,那么它真正能做的就是防止灾难发生。目前,您唯一可以崩溃的是整个过程。

有关详细信息,请参阅

于 2016-08-03T08:30:13.737 回答