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

Swift中用RunLoop实现弱引用对象nil监测是否正确?求替代方案

问题解答

原RunLoop用法不符合需求,原因如下:

  1. RunLoop实例无法持续运行:你手动创建的RunLoop实例没有绑定到任何线程,调用run(mode: .default, before: .now)会因超时时间为当前时间而立即退出,后续的perform闭包根本不会执行。
  2. 一次性执行无法满足持续监测:就算RunLoop能正常运行,perform的闭包也只会执行一次,无法在弱引用对象被释放时及时触发清理操作。
  3. 代码存在笔误:self?.state.listeners应为self?.listeners,否则会访问不存在的state属性。

更合适的方案

方案1:给WeakContainer添加销毁回调(精准触发,推荐)

如果可以修改WeakContainer的实现,给它添加对象销毁时的回调,能在监听对象被释放的瞬间就移除对应条目:

// 修改WeakContainer
class WeakContainer<T: AnyObject> {
    weak var value: T?
    private var onDeinit: (() -> Void)?
    
    init(value: T, onDeinit: (() -> Void)? = nil) {
        self.value = value
        self.onDeinit = onDeinit
    }
    
    deinit {
        onDeinit?()
    }
}

// 修改Process类
class Process<S> {
    typealias Handler = (S) -> ()
    typealias Listener = (WeakContainer<AnyObject>, Handler)
    
    var listeners: [Listener] = []
    
    func addListener(_ object: AnyObject, handler: @escaping Handler) {
        let container = WeakContainer(value: object) { [weak self] in
            // 监听对象被释放时,移除对应listener
            self?.listeners.removeAll { $0.0 === container }
        }
        listeners.append((container, handler))
    }
}

方案2:触发回调前自动清理(无需修改WeakContainer)

如果不能修改WeakContainer,可以在每次通知监听者的时机(比如更新状态时),先清理无效条目:

class Process<S> {
    typealias Handler = (S) -> ()
    typealias Listener = (WeakContainer<AnyObject>, Handler)
    
    var listeners: [Listener] = []
    
    func updateState(_ newState: S) {
        // 先移除已释放对象的监听
        listeners.removeAll { $0.0.value == nil }
        // 通知所有有效监听
        listeners.forEach { $0.1(newState) }
    }
}

方案3:用NSMapTable自动管理(最简洁)

直接使用Foundation提供的NSMapTable,它支持弱引用key,当key对应的对象被释放时会自动移除对应条目,无需手动管理:

class Process<S> {
    typealias Handler = (S) -> ()
    // 弱引用key(监听对象),强引用handler
    private let listeners = NSMapTable<AnyObject, Handler>.weakToStrongObjects()
    
    func addListener(_ object: AnyObject, handler: @escaping Handler) {
        listeners.setObject(handler, forKey: object)
    }
    
    func updateState(_ newState: S) {
        // 遍历所有有效handler并触发
        guard let enumerator = listeners.objectEnumerator() else { return }
        while let handler = enumerator.nextObject() as? Handler {
            handler(newState)
        }
    }
}

内容的提问来源于stack exchange,提问作者Ihor M.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 07:18:29