我问过这个问题 asking 关于局部变量中引用的生命周期的保证并被提及这个线程其中类实例引用生命周期的确切语义进行了讨论。 AFAICT 从该线程来看,类实例可能会在最后一次使用包含最后一个引用的变量之后立即被取消初始化,甚至在同一语句中的其他表达式被求值之前也是如此。有人认为,允许这种行为对于防止在写时复制数据结构中创建不必要的副本是必要的,我可以遵循这一点。
这引起了我的注意,到目前为止我从未见过 Swift 有这样的行为方式。每当我跟踪执行时,无论是在调试还是发布版本中,类实例仅在离开完整范围时才被初始化。
在下面的例子中main()
,我创建了一个类的实例,该实例在其生命周期中打印到控制台。请注意,在调用期间或之后不会使用该实例print()
in main()
:
public final class Something {
init() { print("Something.init()") }
deinit { print("Something.deinit") }
}
func main() {
let something = Something()
print("in main()")
}
main()
当使用调试或发布配置构建和运行此示例时,我看到以下执行顺序,即实例在调用期间保持活动状态print()
in main()
:
$ swift run -c release
Something.init()
in main()
Something.deinit
相反,我期望以下执行顺序:
$ swift run -c release
Something.init()
Something.deinit
in main()
我正在使用随 Xcode 9.2 一起分发的 Swift 编译器 4.0.3:
$ swift --version
Apple Swift version 4.0.3 (swiftlang-900.0.74.1 clang-900.0.39.2)
Target: x86_64-apple-macosx10.9
考虑到 Swift 编译器的开发人员正在尝试非常积极地减少 ARC 引用计数器(以及这种反初始化类实例)以避免写入时复制数据结构中不必要的副本,如何解释这个执行顺序?这里是否还有其他一些优化在起作用,因为在这种情况下保持引用活动实际上并不会导致不必要的工作(只是内存分配的时间比严格必要的时间长)?
我的问题不是解决我遇到的特定问题,毕竟编译器正在创建完全符合语言允许的范围的代码。我想很好地了解 Swift 编译器如何处理引用以及哪些优化在起作用,以便我可以衡量哪些代码模式在关键情况下可以带来良好的性能,例如当需要通过写时复制来复制大型数据结构时,或者涉及大量需要递增和递减的引用时。