首页 > 解决方案 > 如何解释汇编中调用堆栈中的快速(或一般)闭包?

问题描述

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分配局部变量。avalue

但是这个堆栈帧会被弹出吗?看起来确实如此,因为它返回一个值意味着函数已经完成。但另一方面,如果栈帧被弹出,栈帧上的局部变量就消失了,意味着a将被释放,显然不是这样。

那么这整件事是如何运作的呢?

标签: swiftassemblyclosures

解决方案


Swift 代码和汇编代码之间存在很大的“差距”。这两种语言之间的转换是巨大的,所以我建议你不要想“哦等等,这在汇编中是行不通的!” 因为编译器比你想象的要聪明得多。

这里基本上发生的是,由getThis 捕获局部变量重新调整的闭包a。这意味着a那种“进入”闭包。闭包现在有了状态。每当您self在闭包中使用或局部变量时,都会发生这种情况。

这是如何在低抽象层次上实现的?我不太确定编译器究竟是如何工作的,但是这种对变量的捕获通常是通过某种状态机来完成的。因此,您关于a在返回时解除分配的说法getThis可能不正确。a将留在内存中,因为它是由闭包保留的。它是如何做到的?我只能告诉你“编译器魔法”。

无论如何,你还想first表现得如何?未定义的行为?这听起来并不 Swifty。a每次调用都重置为0 first?这与在 中再次声明一个新变量没有什么不同first,是吗?归根结底,这种“捕获”语义似乎在语言中很有用,所以他们这样设计了 Swift。


推荐阅读