捕获列表固定值并不等于深拷贝对象。整数会保留创建闭包时的值;类引用仍指向同一对象。weak是另一种所有权选择,不会让目标保持存活。
原理与设计判断
捕获可变局部变量的闭包可以观察后续变化。[number]这类捕获列表会保留创建时的值。如果要把闭包参数存到函数返回后使用,需要@escaping,但它本身不代表异步执行或内存泄漏。 一手资料
复现代码
同一函数内创建普通捕获和数值快照后,把number改为2;捕获类引用后,把对象值改为3;再释放weak捕获的对象。Queue存储回调,执行一次后清除,让保存期间与调用时点分开。
这个独立示例已于 2026-10-23 使用 Apple Swift 6.4,以 Swift 6 语言模式在 arm64 macOS 目标环境运行。不需要网络或额外软件包。
请在下载文件的文件夹中运行。检查不通过时,precondition 会终止程序。请勿使用 -Ounchecked,因为它会移除这些检查。
swift -swift-version 6 swift-closure-capture-weak-self-escaping.swift
final class Box { var value = 1 }
final class Queue {
private var action: (() -> Int)?
func enqueue(_ action: @escaping () -> Int) { self.action = action }
func run() -> Int? {
let current = action
action = nil
return current?()
}
}
func experiment() {
var number = 1
let live = { number }
let snapshot = { [number] in number }
number = 2
precondition(live() == 2 && snapshot() == 1)
let box = Box()
let capturedReference = { [box] in box.value }
box.value = 3
precondition(capturedReference() == 3)
var temporary: Box? = Box()
let weakRead = { [weak box = temporary] in box?.value }
precondition(weakRead() == 1)
temporary = nil
precondition(weakRead() == nil)
let queue = Queue()
queue.enqueue(snapshot)
precondition(queue.run() == 1 && queue.run() == nil)
print("variable=2 snapshot=1 captured-object=3 weak-after-release=nil queued=1 then=nil")
}
experiment()
运行结果与解读
variable=2 snapshot=1 captured-object=3 weak-after-release=nil queued=1 then=nil
普通捕获返回2,数值快照返回1,捕获的对象引用看到修改后的3。weak在目标释放后返回nil。Queue第一次返回1,第二次返回nil。这个Queue是同步存储示例,不是异步调度器。
适用范围与限制
捕获不一定造成堆分配,编译器可能优化存储方式。[weak self]不是每个闭包的固定前缀,还要判断拥有者消失后是否允许跳过工作。确认循环时应检查直接和间接的所有权路径。
接下来要确认的事
记录谁持有回调、何时清除,以及取消的意义。如果工作必须让拥有者存活,就不要机械地改用弱引用。真正的所有权循环如何切断,可参考ARC实验。
来源与验证
- The Swift Programming Language: ClosuresSwift.org · 官方文档 · 核查 2026年10月23日依据: 实现契约与说明的依据。运行输出另由本文所附实验确认。

