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
所以“析构函数”被调用 – 这意味着(在我的理解中)堆栈展开在Swift中起作用.
任何人都可以解释为什么文档说它是
not involved
?
堆栈展开只是导航堆栈中寻找处理程序的过程.维基百科 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错误不会解除堆栈查找处理程序的问题.它只是返回,并期望调用者处理抛出的错误.事实上,你引用goes on to say之后的句子:
As such, the performance characteristics of a
throw
statement are comparable to those of areturn
statement.
所以,使用第一个例子,其中f调用g来调用h,在Swift中,如果你想要f来捕获由h抛出的错误,那么:
> h必须明确标记为抛出错误;
> g必须明确地尝试调用h;
> g也必须标记它也会引发错误;和
> f必须明确地尝试调用g.
简而言之,虽然其他一些语言在查找异常处理程序的过程中提供堆栈展开,但在Swift错误处理中,您必须显式捕获您尝试的函数抛出的错误,或者将其指定为抛出的函数,以便尝试调用失败将被抛回调用者.在Swift中没有自动展开堆栈.
所有这些都与是否发生解除分配无关.正如您所见,是的,Swift中的抛出与返回非常相似,可以释放那些局部变量.
值得注意的是,并非所有涉及堆栈展开的异常处理都会进行重新分配.通常它确实如此(因为当我们处理异常时我们希望它清理),但是例如,“当发生未处理的异常时,GNU C unwinder不会调用对象析构函数.原因是为了提高可调试性. “ (从Exception Handling in LLVM开始.)显然,这仅适用于调试环境中的未处理异常,但它说明了展开堆栈并不一定意味着对象被解除分配的问题.