我想从一些“深度”函数中抛出一个异常,所以它会冒泡到另一个函数,我想抓住它。
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
不会在调用链中一直泄漏?
答案 0 :(得分:30)
是的,有可能!
使用:fatalError("your message here")
抛出运行时异常
答案 1 :(得分:6)
Swift中的错误处理机制不涉及提升未检查(运行时)异常。相反,需要显式错误处理。 Swift肯定不是最近为此设计设计的唯一语言 - 例如Rust和Go也以自己的方式也需要在代码中明确描述错误路径。在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个优化级别:
-Onone
:没有优化; 调试构建-O
:正常优化; 发布构建-O SWIFT_DISABLE_SAFETY_CHECKS
:未经检查的优化; 极度优化构建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)
}
preconditionFailure(_:)
如果您确定某些条件(在文档中等)未得到满足,请写下此行。 这与assertionFailure(_:)
类似,但在发布版本和调试版本中都有效。
与assertionFailure(_:)
一样,这个有一个名为precondition(_:_:)
的姐妹函数,它允许您在运行时决定是否满足前提条件。 preconditionFailure(_:)
本质上就是这个,但假设一旦程序到达该行,就永远不会满足前提条件。
guard index >= 0 else {
preconditionFailure("You passed a negative number as an array index")
return nil
}
请注意,在极其优化的版本中,未定义如果此行被点击会发生什么!因此,如果您不希望自己的应用在可能遇到此问题时退出,那么确保错误状态可以处理。
fatalError(_:)
用作最后的手段。当其他每一次挽救这一天的尝试失败时,这就是你的核武器。打印传递给它的消息(连同文件和行号)后,程序停止运行。
一旦程序到达此行,此行始终运行,程序永远不会继续。 即使在极其优化的版本中也是如此。
#if arch(arm) || arch(arm64)
fatalError("This app cannot run on this processor")
#endif
答案 3 :(得分:0)
Java中没有
RuntimeException
并没有在调用链中泄漏的概念吗?
Swift确实具有错误处理功能,在编译时不会出错。
但是,在讨论这些内容之前,我必须说的是,您指出的是使用该语言的throws
,do...catch
,try
和throw
关键字的地方/ 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 2017个guard let fileSizeInBytes = try? FileManager.default.attributesOfItem(atPath: path)[.size] as? Int64 else {
assertionFailure("Couldn't get file size")
return false
}
,assertion
和precondition
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()
就属于这个家庭。