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

Swift中使用combine函数是否真的避免了循环引用?

关于Swift闭包循环引用的疑问

我在观看Sundel的Swift教学视频时,看到他分享的优雅代码技巧。最初他用[weak self]配合guard let self ...的写法避免闭包循环引用,代码如下:

// A view controller that currently captures 'self' weakly, in
// order to call a method on a 'productManager' property object:
class ProductViewController: UIViewController {
    ...

    override func viewDidLoad() {
        super.viewDidLoad()

        buyButton.handler = { [weak self] in
            guard let self = self else {
                return
            }

            self.productManager.startCheckout(for: self.product)
        }
    }
}

随后他创建了一个combine工具方法:

// Introducing a 'combine' function for applying a value to
// any function or closure:
func combine<A, B>(
    _ value: A,
    with closure: @escaping (A) -> B
) -> () -> B {
    return { closure(value) }
}

并把初始代码替换成了这样:

// Using our new combine function:
class ProductViewController: UIViewController {
    ...

    override func viewDidLoad() {
        super.viewDidLoad()
        
        buyButton.handler = combine(product,
            with: productManager.startCheckout
        )
    }
}

他表示这段代码因为没有通过self引用product或productManager,所以不存在循环引用。但我认为这段代码等价于隐式使用self的写法,依然存在循环引用。想确认他的观点是否正确?如果正确,麻烦解释combine函数是如何避免循环引用的。


解答

Sundel的观点是正确的,重构后的代码确实不存在循环引用,具体原因如下:

1. 原代码的循环引用风险根源

原闭包中,我们通过self.productManager和self.product访问属性,这会导致闭包捕获self(即便后续用weak self处理)。潜在的循环引用链条是:ProductViewController → buyButton → 闭包 → self(ProductViewController),形成闭环。

2. 重构后代码的捕获逻辑变化

重构后的代码里:

  • 写product时Swift虽会隐式推断为self.product,但此时闭包(combine返回的闭包)捕获的是product这个实例本身,而非self;
  • productManager.startCheckout是方法引用,捕获的是productManager实例,同样不是self。

简单说,闭包的捕获目标从self变成了product和productManager两个独立对象。

3. combine函数的核心作用

combine的本质是把一个值和一个接受该值的闭包打包成无参闭包:

  • 它接收的value是product实例,closure是productManager.startCheckout(柯里化后签名为(Product) -> Void);
  • 返回的闭包仅持有product和productManager,完全不涉及self。

4. 为什么不会形成循环引用?

循环引用需要双向强引用闭环:

  • 假设buyButton强持有handler闭包,闭包强持有productManager和product;但通常productManager和product不会持有ProductViewController(除非业务逻辑特殊设计),链条无法闭环。
  • 而原代码中闭包捕获self,self又持有buyButton,才会产生闭环风险。

综上,重构后的代码通过直接捕获属性实例而非self,切断了可能形成循环引用的链条,因此不存在循环引用问题。


内容的提问来源于stack exchange,提问作者ppalancica

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 14:06:23