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
相关产品推荐
相关产品推荐

