未捕获 Error/Exception 在 Swift 中处理
Uncaught Error/Exception Handling in Swift
我知道 Cocoa 中有一个 UncaughtExceptionHandler,但是我正在为 Swift 寻找同样的东西。即,每当应用程序中有任何 error/exception 由于任何错误而未在本地捕获时,它应该一直冒泡到顶级应用程序对象,我应该能够优雅地处理它并适当地响应用户。
Swift 没有捕获所有任意运行时异常的机制。
原因在
中解释
在 swift-用户论坛中。摘录:
Swift made a conscious choice not to include exceptions thrown through
arbitrary stack frames not because it was technically impossible, but
because its designers judged the costs to be too high.
The problem is this: if a piece of code is going to exit early because
of an error, it has to be written to handle that early exit. Otherwise
it will misbehave—fail to deallocate memory, fail to close file
handles/sockets/database connections/whatever, fail to release locks,
etc. In a language like Java, writing truly exception-safe code
requires a ridiculous quantity of try/finally blocks. That's why
nobody does it. They make judgements about which exceptions they're
likely to see and which resources are dangerous to leak, and only
protect their code against those specific anticipated conditions. Then
something unforeseen happens and their program breaks.
This is even worse in a reference-counted language like Swift because
correctly balancing the reference counts in the presence of exceptions
basically requires every function to include an implicit finally block
to balance all the retain counts. This means the compiler has to
generate lots of extra code on the off chance that some call or
another throws an exception. The vast majority of this code is never,
ever used, but it has to be there, bloating the process.
Because of these problems, Swift chose not to support traditional
exceptions; instead, it only allows you to throw errors in
specially-marked regions of code. But as a corollary, that means that,
if something goes really wrong in code that can't throw, all it can
really do to prevent a disaster is crash. And currently, the only
thing you can crash is the entire process.
有关详细信息,请参阅
这是我用来记录所有 exceptions/errors 的代码。 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
}
我知道 Cocoa 中有一个 UncaughtExceptionHandler,但是我正在为 Swift 寻找同样的东西。即,每当应用程序中有任何 error/exception 由于任何错误而未在本地捕获时,它应该一直冒泡到顶级应用程序对象,我应该能够优雅地处理它并适当地响应用户。
Swift 没有捕获所有任意运行时异常的机制。 原因在
中解释在 swift-用户论坛中。摘录:
Swift made a conscious choice not to include exceptions thrown through arbitrary stack frames not because it was technically impossible, but because its designers judged the costs to be too high.
The problem is this: if a piece of code is going to exit early because of an error, it has to be written to handle that early exit. Otherwise it will misbehave—fail to deallocate memory, fail to close file handles/sockets/database connections/whatever, fail to release locks, etc. In a language like Java, writing truly exception-safe code requires a ridiculous quantity of try/finally blocks. That's why nobody does it. They make judgements about which exceptions they're likely to see and which resources are dangerous to leak, and only protect their code against those specific anticipated conditions. Then something unforeseen happens and their program breaks.
This is even worse in a reference-counted language like Swift because correctly balancing the reference counts in the presence of exceptions basically requires every function to include an implicit finally block to balance all the retain counts. This means the compiler has to generate lots of extra code on the off chance that some call or another throws an exception. The vast majority of this code is never, ever used, but it has to be there, bloating the process.
Because of these problems, Swift chose not to support traditional exceptions; instead, it only allows you to throw errors in specially-marked regions of code. But as a corollary, that means that, if something goes really wrong in code that can't throw, all it can really do to prevent a disaster is crash. And currently, the only thing you can crash is the entire process.
有关详细信息,请参阅
这是我用来记录所有 exceptions/errors 的代码。 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
}