【问题标题】:Uncaught Error/Exception Handling in SwiftSwift 中未捕获的错误/异常处理
【发布时间】:2016-12-08 20:06:36
【问题描述】:

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

Android 有。 Flex 有。 Java 有。想知道为什么 Swift 缺少这个关键特性。

【问题讨论】:

  • NSSetUncaughtExceptionHandler 在 Swift 中可用:stackoverflow.com/questions/25441302/…。但是,它只捕获 Objective-C 异常,而不捕获 Swift 运行时错误或 thrown 错误。
  • 感谢@MartinR 的快速回复。我知道 NSSetUncaughtExceptionHandler 它处理 Objective-C 异常。但是我正在为 Swift 寻找相同/相似的东西。
  • Swift 没有机制来捕获所有类型的运行时错误。我只能猜测原因。例如,它不适用于自动引用计数。在 swift.org 的邮件列表之一中,您可能会从 Swift 开发人员那里获得更好的回复。
  • 再次感谢@MartinR ...您知道事件/异常/错误是否通过 Swift 中的隧道和冒泡阶段 .. 我只是在想如果异常通过隧道或冒泡阶段,我可以抓住应用程序级别 AppDelegate 的异常。
  • @SunnyTambi 很好的问题你有什么解决方法吗?

标签: swift uncaughtexceptionhandler


【解决方案1】:

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

在 swift-users 论坛中。摘录:

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

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

这在像 Swift 这样的引用计数语言中更糟糕,因为 在存在异常的情况下正确平衡引用计数 基本上要求每个函数都包含一个隐式 finally 块 平衡所有保留计数。这意味着编译器必须 如果有人调用或生成大量额外代码 另一个抛出异常。这段代码的绝大多数永远不会, 曾经用过,但它必须在那里,使过程膨胀。

因为这些问题,Swift 选择不支持传统的 例外;相反,它只允许您在 特殊标记的代码区域。但作为推论,这意味着, 如果不能抛出的代码真的出错了,它可以 真正做到防止灾难的是崩溃。而目前,唯一 你可以崩溃的是整个过程。

有关详细信息,请参阅

【讨论】:

  • 感谢@MartinR ...我发现 Swift 开发人员已经“按设计”将处理每个大小异常和错误的负担交给了开发人员。
  • 非常感谢您的分享,现在至少对于似乎令人费解的遗漏的理由很清楚了。
  • 这对我来说很好,但我仍然需要至少能够捕获信号,以便我可以实现我的崩溃记录器。似乎无法使其工作;强制展开 nil 会抛出一个EXC_BAD_INSTRUCTION,我无法用异常或信号处理程序捕获它......
【解决方案2】:

这是我用来记录所有异常/错误的代码。 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
}

【讨论】:

  • 非常感谢,甚至不知道信号(...)函数
  • 请注意,只有“异步信号安全”函数可以安全地从信号处理程序中调用。 “printf”和相关函数不是异步信号安全的。
  • 您在哪里使用此代码?应用代理?主要的?我试图将此代码添加到我的应用程序中,但没有成功。
  • 不幸的是,它对我不起作用(MacOS 应用程序)
  • let x = [1]; print(x[5]) 没有被抓到,是否有机会进行快速错误处理?
猜你喜欢
  • 1970-01-01
  • 2017-05-10
  • 2015-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-25
相关资源
最近更新 更多