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 错误处理中,您必须显式 catchtry,或指定为 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。)显然,这仅适用于调试环境中未处理的异常, 但它说明了展开堆栈并不一定意味着对象被释放的问题。