swift - 如何解释汇编中调用堆栈中的快速(或一般)闭包?
问题描述
class Assembly {
func getThis(value: Int) -> () -> () {
var a = value
return {
a += 1
print(a)
}
}
}
let assembly = Assembly()
let first = assembly.getThis(value: 5)
first()
first()
let second = assembly.getThis(value: 0)
second()
second()
first()
second()
给出以下值:
6
7
1
2
8
3
我知道这是意料之中的,我只是想了解它是如何工作的。我知道堆栈在汇编中如何工作的基础知识。我认为当被调用时,一个新的堆栈帧被推送并通过分配getThis
分配局部变量。a
value
但是这个堆栈帧会被弹出吗?看起来确实如此,因为它返回一个值意味着函数已经完成。但另一方面,如果栈帧被弹出,栈帧上的局部变量就消失了,意味着a
将被释放,显然不是这样。
那么这整件事是如何运作的呢?
解决方案
Swift 代码和汇编代码之间存在很大的“差距”。这两种语言之间的转换是巨大的,所以我建议你不要想“哦等等,这在汇编中是行不通的!” 因为编译器比你想象的要聪明得多。
这里基本上发生的是,由getThis
捕获局部变量重新调整的闭包a
。这意味着a
那种“进入”闭包。闭包现在有了状态。每当您self
在闭包中使用或局部变量时,都会发生这种情况。
这是如何在低抽象层次上实现的?我不太确定编译器究竟是如何工作的,但是这种对变量的捕获通常是通过某种状态机来完成的。因此,您关于a
在返回时解除分配的说法getThis
可能不正确。a
将留在内存中,因为它是由闭包保留的。它是如何做到的?我只能告诉你“编译器魔法”。
无论如何,你还想first
表现得如何?未定义的行为?这听起来并不 Swifty。a
每次调用都重置为0 first
?这与在 中再次声明一个新变量没有什么不同first
,是吗?归根结底,这种“捕获”语义似乎在语言中很有用,所以他们这样设计了 Swift。