保证局部变量中引用的生命周期

Guarantees about the lifetime of a reference in a local variable

Swift中,我可以使用ARC机制来管理进程外部资源的生命周期,因为实例的 classes 可预测地取消初始化。这与 Java 运行时等环境形成对比,在运行时环境中,当垃圾收集器收集对象时实例被取消初始化,这不能保证在定义的时间内发生 window.

但是,当局部变量引用这些实例时,Swift 语言和运行时对实例的生命周期做出的确切保证是什么?例如。当局部变量持有对实例的唯一引用时,最早可以释放实例的时间点是什么?

在下面的示例中,我创建了一个 class 的实例,并将对它的引用存储在局部变量中。

public final class Something {
    init() { print("something.init()") }
    deinit { print("something.deinit()") }
}

func useSomething() {
    let something = Something()
    print("useSomething()")
}

useSomething()

在我打印 useSomething() 之后不再使用该变量,但是 deinit 在调用 print() 之后 运行:

$ swift run -c release
something.init()
useSomething()
something.deinit()

似乎引用总是在变量超出范围的地方递减。将变量声明包装在 do 块中会更改顺序:

func useSomething() {
    do { let something = Something() }
    print("useSomething()")
}

$ swift run -c release
something.init()
something.deinit()
useSomething()

这个顺序是有保证的还是可以随着不同的编译器或优化级别而改变?


我对此感兴趣的原因是我想将 C APIs 包装在面向对象的 Swift APIs 中,并希望自动管理 C APIs 的生命周期使用 C API 使用 Swift classes 和引用计数分配的资源。如果 C API 的每次使用都需要对其操作的资源的引用,这会很好用,因为我知道 Swift 实例至少会存在到最后一次调用该实例所在的资源代表.

但是一些 APIs 使用全局状态来 select 资源和随后对 API 的调用不需要对要传递的资源的引用并隐式操作selected 资源。 OpenGL 的 glDrawElements() 隐式使用了可能 5 或 10 个这样的资源(顶点数组、着色器、帧缓冲区、纹理……)。

Swift 不保证对象的生命周期,直到 最近的周围范围的末端,例如参见 Swift 论坛中的以下主题:

说明您可以使用 withExtendedLifetime(_:_:):

Evaluates a closure while ensuring that the given instance is not destroyed before the closure returns.

为了这个目的。至于道理, Dave Abrahams (Apple) 状态:

The lack of such a guarantee, which is very seldom actually useful anyhow, is what allows us to turn costly copies (with associated refcount traffic and, often CoW allocation and copying fallout) into moves, which are practically free. Adopting it would basically kill our performance story for CoW.

Joe Groff (Apple)在同一个线程中:

Yeah, if you want to vend resources managed by an object to consumers outside of that object like this, you need to use withExtendedLifetime to keep the object alive for as long as you're using the resources. A cleaner way to model this might be to put the class or protocol in control of handling the I/O to the file handle, instead of vending the file handle itself, so that the ownership semantics fall out more naturally:

更新(2022 年 1 月): 目前正在讨论是否为对象引入词法生命周期。有关详细信息,请参阅 Swift 论坛中的 A roadmap for improving Swift performance predictability: ARC improvements and ownership control