Swift 4/5中如何让闭包捕获对象方法时避免循环引用
在Swift 4/5中让闭包持有类自动处理弱引用避免循环引用
问题分析
你遇到的核心痛点是:客户端在传递自身方法给CallbackHolder时,必须手动添加[weak self]来打破循环引用,既繁琐又容易遗漏。我们的目标是把弱引用的处理逻辑封装到CallbackHolder内部,让客户端用最直观的方式调用。
解决方案:泛型弱回调封装
我们可以创建一个泛型的弱回调包装类,让CallbackHolder持有这个包装实例,而非直接持有闭包。这样就能自动对目标对象进行弱引用,从根源上避免循环。
步骤1:定义弱回调包装类
这个类负责持有目标对象的弱引用,以及对应的方法引用:
class WeakCallback<T: AnyObject> { weak var target: T? let action: (T) -> (String) -> Void init(target: T, action: @escaping (T) -> (String) -> Void) { self.target = target self.action = action } func call(_ value: String) { // 只有当目标对象还存在时,才调用方法 guard let target = target else { return } action(target)(value) } }
步骤2:修改CallbackHolder类
让CallbackHolder变成泛型类,接收目标对象和方法引用,内部创建WeakCallback实例:
class CallbackHolder<T: AnyObject> { private let weakCallback: WeakCallback<T> // 初始化方法直接接收目标对象和方法,无需客户端手动写闭包 init(target: T, action: @escaping (T) -> (String) -> Void) { self.weakCallback = WeakCallback(target: target, action: action) } func useCallback() { weakCallback.call("Hi!") } }
步骤3:客户端简化使用
现在客户端只需要直接传递自身和方法即可,完全不用处理弱引用:
class Client { func callback(string: String) { print(string) } // 直接传递self和方法,无需手动添加[weak self] lazy var callbackOwner = CallbackHolder(target: self, action: Client.callback) deinit { print("deinit") } }
测试验证
运行你的测试函数:
func test() { Client().callbackOwner.useCallback() }
会依次打印Hi!和deinit,说明回调正常执行,且没有循环引用。
方案原理
这个方案的核心是把“对象+方法”的组合封装到WeakCallback中,其中对目标对象使用弱引用。引用链变成:Client强引用CallbackHolder → CallbackHolder强引用WeakCallback → WeakCallback弱引用Client
这样就打破了原来的循环引用,同时客户端无需感知任何弱引用逻辑,使用体验更简洁。
额外优化:支持更灵活的方法签名
如果需要支持不同参数的回调,可以把WeakCallback和CallbackHolder的方法签名改成泛型参数,比如:
class WeakCallback<T: AnyObject, Input> { weak var target: T? let action: (T) -> (Input) -> Void init(target: T, action: @escaping (T) -> (Input) -> Void) { self.target = target self.action = action } func call(_ value: Input) { guard let target = target else { return } action(target)(value) } } class CallbackHolder<T: AnyObject, Input> { private let weakCallback: WeakCallback<T, Input> init(target: T, action: @escaping (T) -> (Input) -> Void) { self.weakCallback = WeakCallback(target: target, action: action) } func useCallback(with value: Input) { weakCallback.call(value) } }
这样CallbackHolder就可以支持任意输入类型的回调,通用性更强。
内容的提问来源于stack exchange,提问作者Phlippie Bosman
相关产品推荐
相关产品推荐

