You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:37:25