Swift闭包弱捕获自身变量为何返回nil?如何处理循环引用?
Swift闭包捕获与循环引用的场景解析
场景1:强捕获导致循环引用
// Foo.swift class Foo { let action: (() -> Void)? init (action: (() -> Void)?) { self.action = action } func doSomething() { print("¯\\_(ツ)_/¯") } } // Bar.swift var leaky: Foo! leaky = Foo() { leaky.doSomething() // 会正常打印,但存在循环引用 } // Bar.swift 某处 leaky.action?()
这里leaky是隐式解包可选变量,闭包强捕获变量本身(而非变量当前的临时值)。当闭包被赋值给Foo实例的action属性后,形成循环引用链:Foo实例 → 闭包 → leaky变量 → Foo实例,导致实例永远无法被释放。由于强捕获跟踪变量的最新值,后续leaky被赋值为实例时,闭包执行能拿到该值,因此方法调用正常生效。
场景2:弱捕获为什么始终是nil
// Bar.swift var leaky: Foo! leaky = Foo() { [weak leaky] in leaky?.doSomething() // leaky为nil,不会执行打印 }
[weak leaky]捕获的是闭包创建时变量的当前值,而非变量本身。在闭包创建的瞬间,leaky还未被赋值(Foo()的初始化操作尚未完成,赋值给leaky的步骤在初始化之后),此时leaky的取值是nil,闭包里的弱引用自然也是nil。后续leaky被赋值为实例时,闭包内的弱引用不会同步更新,因此调用时无法执行方法。
场景3:带参数的闭包无循环引用
// Foo.swift class Foo { let action: ((Foo) -> Void)? init(action: ((Foo) -> Void)?) { self.action = action } func doSomething() { print("¯\\_(ツ)_/¯") } } // Bar.swift let leaky: Foo = Foo() { param in param.doSomething() // 正常执行 } // Bar.swift 某处 leaky.action?(leaky)
这个场景里,闭包不捕获任何外部变量,仅在执行时通过参数接收Foo实例。调用leaky.action?(leaky)时,实例作为临时参数传入闭包,闭包仅在执行期间持有实例的临时引用,执行完毕后引用立即释放。同时Foo实例持有闭包,但闭包不持有实例的引用,完全不存在循环引用的条件,实例可以正常被释放。
关于你的假设验证
你的假设方向完全正确,补充细节后更准确:
- 强捕获的是变量本身,因此能跟踪到后续赋值的最新值;
- 弱捕获的是闭包创建时变量的当前值,此时
leaky尚未完成赋值,所以捕获到nil; - 参数传递方式下,闭包不依赖外部捕获,仅在执行时接收实例引用,从根源上避免了循环引用。
内容的提问来源于stack exchange,提问作者Phineas
相关产品推荐
相关产品推荐

