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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:39:45