Swift 中的错误处理不涉及堆栈展开。这是什么意思?
Error handling in Swift does not involve stack unwinding. What does it mean?
根据文档,
error handling in Swift does not involve unwinding the call stack, a
process that can be computationally expensive
不知这是什么意思?我一直认为 堆栈展开 是在异常情况下正确调用析构函数的过程(就 C++ 而言)。
所以我决定模拟这种情况:
class A {
init() {
print("Inited")
}
deinit {
print("Deinited")
}
}
func f() throws {
let a = A()
throw MyError.e
}
输出为:
Inited
Deinited
所以 "destructor" 被调用 - 这意味着(在我的理解中)堆栈展开在 Swift 中工作。
任何人都可以解释为什么文档说它是
not involved
?
确实,如果 swift 堆栈展开,那么它将调用自块开始以来分配的所有对象的所有析构函数。但反过来可能并不正确。仅仅因为为您的对象 A
调用了析构函数 而不是 就意味着 swift 堆栈展开。另外,如果你真的想测试它是否是堆栈展开,你应该尝试一个更严格的例子
堆栈展开只是向上导航堆栈以查找处理程序的过程。维基百科 summarizes it as follows:
Some languages call for unwinding the stack as this search progresses. That is, if function f
, containing a handler H
for exception E
, calls function g
, which in turn calls function h
, and an exception E
occurs in h
, then functions h
and g
may be terminated, and H
in f
will handle E
.
而 Swift 错误不会展开堆栈以寻找处理程序。它只是 returns,并期望调用者处理抛出的错误。事实上,你引用的那句话之后的句子 goes on to say:
As such, the performance characteristics of a throw
statement are comparable to those of a return
statement.
因此,使用第一个示例,其中 f
调用 g
,在 Swift 中调用 h
,如果您希望 f
捕获h
引发的错误,然后:
h
必须显式标记它 throws
错误;
g
必须显式 try
它调用 h
;
g
也必须标明它 throws
也有错误;和
f
必须显式 try
它对 g
的调用。
简而言之,虽然其他一些语言在查找异常处理程序的过程中提供堆栈展开,但在 Swift 错误处理中,您必须显式 catch
您 try
,或指定为 throws
的函数,以便失败的 try
调用将返回给调用者。 Swift 中没有自动展开堆栈。
所有这些都与是否发生释放无关。如您所见,是的,Swift 中的 throw
的行为很像 return
,释放那些局部变量。
值得注意的是,并非所有涉及堆栈展开的异常处理都会进行释放。通常它确实如此(因为我们当然希望它在处理异常时进行清理),但是例如 "the GNU C++ unwinder does not call object destructors when an unhandled exception occurs. The reason for this is to improve debuggability."(来自 Exception Handling in LLVM。)显然,这仅适用于调试环境中未处理的异常, 但它说明了展开堆栈并不一定意味着对象被释放的问题。
根据文档,
error handling in Swift does not involve unwinding the call stack, a process that can be computationally expensive
不知这是什么意思?我一直认为 堆栈展开 是在异常情况下正确调用析构函数的过程(就 C++ 而言)。
所以我决定模拟这种情况:
class A {
init() {
print("Inited")
}
deinit {
print("Deinited")
}
}
func f() throws {
let a = A()
throw MyError.e
}
输出为:
Inited
Deinited
所以 "destructor" 被调用 - 这意味着(在我的理解中)堆栈展开在 Swift 中工作。
任何人都可以解释为什么文档说它是
not involved
?
确实,如果 swift 堆栈展开,那么它将调用自块开始以来分配的所有对象的所有析构函数。但反过来可能并不正确。仅仅因为为您的对象 A
调用了析构函数 而不是 就意味着 swift 堆栈展开。另外,如果你真的想测试它是否是堆栈展开,你应该尝试一个更严格的例子
堆栈展开只是向上导航堆栈以查找处理程序的过程。维基百科 summarizes it as follows:
Some languages call for unwinding the stack as this search progresses. That is, if function
f
, containing a handlerH
for exceptionE
, calls functiong
, which in turn calls functionh
, and an exceptionE
occurs inh
, then functionsh
andg
may be terminated, andH
inf
will handleE
.
而 Swift 错误不会展开堆栈以寻找处理程序。它只是 returns,并期望调用者处理抛出的错误。事实上,你引用的那句话之后的句子 goes on to say:
As such, the performance characteristics of a
throw
statement are comparable to those of areturn
statement.
因此,使用第一个示例,其中 f
调用 g
,在 Swift 中调用 h
,如果您希望 f
捕获h
引发的错误,然后:
h
必须显式标记它throws
错误;g
必须显式try
它调用h
;g
也必须标明它throws
也有错误;和f
必须显式try
它对g
的调用。
简而言之,虽然其他一些语言在查找异常处理程序的过程中提供堆栈展开,但在 Swift 错误处理中,您必须显式 catch
您 try
,或指定为 throws
的函数,以便失败的 try
调用将返回给调用者。 Swift 中没有自动展开堆栈。
所有这些都与是否发生释放无关。如您所见,是的,Swift 中的 throw
的行为很像 return
,释放那些局部变量。
值得注意的是,并非所有涉及堆栈展开的异常处理都会进行释放。通常它确实如此(因为我们当然希望它在处理异常时进行清理),但是例如 "the GNU C++ unwinder does not call object destructors when an unhandled exception occurs. The reason for this is to improve debuggability."(来自 Exception Handling in LLVM。)显然,这仅适用于调试环境中未处理的异常, 但它说明了展开堆栈并不一定意味着对象被释放的问题。