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

SwiftUI List搭配Firebase监听childAdded时出现内存泄漏问题求助

内存泄漏根因定位
  • 未主动移除Firebase数据库监听:Firebase Realtime Database的observe系列方法注册的事件监听会被SDK内部长期持有,必须显式调用移除方法才会释放。你当前的代码只注册了childAdded类型的监听,没有做任何移除操作,而DatabaseManager是全局单例,生命周期和应用一致,注册的监听会永久存在——哪怕ChatView、ChatViewModel已经被销毁,监听关联的闭包、闭包捕获的变量都会持续占用内存,这是泄漏的核心诱因。你在闭包中使用[weak self]只是避免了闭包持有ChatViewModel导致ViewModel无法释放的问题,并没有解决SDK持有监听本身带来的泄漏。
  • 未处理重复注册场景:如果多次进入同一会话、或者切换会话,代码会重复注册多个监听,不仅会加剧内存泄漏,还会出现同一条消息被多次回调的异常。
修复方案

步骤1:改造DatabaseManager的监听逻辑

给监听方法新增返回值(返回Firebase的监听句柄),同时新增移除监听的接口:

// 监听方法新增返回UInt类型的监听句柄
func observeMessagesForConversation(conversationId id :String,completion : @escaping ([MessageModel]) -> Void ) -> UInt {
    return database.child(id).child("messages").observe(.childAdded) { snapshot in
        guard let value = snapshot.value as? [String:Any] else {
            completion([])
            return
        }
        let decoder = JSONDecoder()
        guard
            let jsonData = try? JSONSerialization.data(withJSONObject:value),
            let message = try? decoder.decode(MessageModel.self, from: jsonData) else {
            completion([])
            return
        }
        completion([message])
    }
}

// 新增移除指定会话消息监听的方法
func removeMessageObserver(conversationId id: String, handle: UInt) {
    database.child(id).child("messages").removeObserver(withHandle: handle)
}

步骤2:改造ChatViewModel逻辑

新增属性存储当前监听句柄,在切换会话、ViewModel销毁时主动移除监听:

class ChatViewModel : ObservableObject  {
    @Published private(set) var messages : [MessageModel] = []
    private(set) var conversationID : String? = nil
    // 存储当前消息监听的句柄
    private var messageObserverHandle: UInt?

    func setConverationID(convesationID : String?){
        // 切换会话前先移除旧的监听,避免重复注册
        removeCurrentObserver()
        
        guard let convesationID = convesationID else {
            print("CONVERSATION ID DOES NOT EXIST")
            return
        }
        self.conversationID = convesationID
        startObservingConversation()
    }

    private func startObservingConversation(){
        guard let conversationID = self.conversationID else {
            return
        }
        // 保存监听返回的句柄
        messageObserverHandle = DatabaseManager.shared.observeMessagesForConversation(conversationId: conversationID) { [weak self] message in
            self?.messages += message
        }
    }
    
    // 移除当前已注册的监听
    private func removeCurrentObserver() {
        if let handle = messageObserverHandle, let conversationID = conversationID {
            DatabaseManager.shared.removeMessageObserver(conversationId: conversationID, handle: handle)
            messageObserverHandle = nil
        }
    }

    // ViewModel销毁时自动移除监听
    deinit {
        removeCurrentObserver()
    }
}

改造完成后,当ChatView页面退出销毁时,@StateObject修饰的ChatViewModel会同步被销毁,触发deinit方法自动移除数据库监听,即可彻底解决本次内存泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 00:18:04