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

Swift开发iOS App集成SignalR出现内存泄漏致崩溃问题

问题场景

在Swift开发的iOS应用中使用signalr实现数值实时更新功能,SignalRService类实现代码如下:

public class SignalRService {
    
    private var connection: HubConnection
    
    private let intIndesiesArgument : String = "argument1,argument2,..."
    private let commoditiesArgument : String = "argument1,argument2,..."
    
    init(url: URL,delegate : SignalRDataUpdateDelegate) {
        connection = HubConnectionBuilder(url: url)
            .withLogging(minLogLevel: .info)
            .withAutoReconnect()
            .build()
        connection.delegate = self
        
        
        connection.on(method: Events.PricesUpdated.rawValue, callback: { response in
            do {
                
                let channel = try response.getArgument(type: String.self)
                if channel == Channels.commodities.rawValue{
                    if response.hasMoreArgs() {
                        let dataModel : CommoditiesModel = try response.getArgument(type: CommoditiesModel.self)
                        delegate.updateComodities(dataModel: dataModel)
                    }
                    
                }else{
                    if response.hasMoreArgs() {
                        let dataModel : CommoditiesModel = try response.getArgument(type: CommoditiesModel.self)
                        delegate.updateIntIndecies(dataModel: dataModel)
                    }
                }
                
            } catch {
                //print(error)
            }
        })
        
        connection.start()
        
    }
    
    func disconnectSignalR(){
        connection.stop()
    }
    
    private func handleMessage(_ message: String, from user: String) {
        // Do something with the message.
        print(message)
    }
    
}

extension SignalRService : HubConnectionDelegate{
    
    public func connectionDidOpen(hubConnection: HubConnection) {
        
        connection.invoke(method: Events.Subscribe.rawValue, arguments:[Channels.commodities.rawValue,commoditiesArgument]) { error in
            if let e = error {
                //self.appendMessage(message: "Error: \(e)")
                print(e)
            }else{
                print("Succefully subscribe")
            }
            
        }
        
        connection.invoke(method: Events.Subscribe.rawValue, arguments:[Channels.intlIndices.rawValue,intIndesiesArgument]) { error in
            
            if let e = error {
                //self.appendMessage(message: "Error: \(e)")
                print(e)
            }else{
                print("Succefully subscribe")
            }

        }
        print(hubConnection.connectionId ?? "")
    }
    
    public func connectionDidFailToOpen(error: Error) {
        print(error.localizedDescription)
    }
    
    public func connectionDidClose(error: Error?) {
        print(error?.localizedDescription ?? "")
    }
    
    
}

在ViewController中按如下逻辑使用服务:视图控制器变为活跃状态时建立连接,视图消失时断开连接,实现代码如下:

class MyViewController : BaseViewController , SignalRDataUpdateDelegate {

  private var connection: SignalRService?

    @objc func didBecomeActive() {
        connection = SignalRService(url: URL(string: "https://myURL.com/update")!, 
          delegate: self)
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        connection?.disconnectSignalR()
    }

}

功能可正常运行并更新数值,但存在异常:应用每秒收到新值时内存占用持续攀升,直到超出内存上限后应用崩溃,抛出异常信息如下:

RESOURCE RESOURCE_TYPE_MEMORY (limit=2098 MB, unused=0x0)

问题原因及修复方案

内存持续上涨是循环引用+重复注册回调/重复创建连接导致的,按以下几点逐一修复即可:

  • 闭包循环引用导致实例无法释放
    connection.on(method:)注册的回调闭包、invoke方法的回调闭包均强引用内部持有的对象,而connection本身是SignalRService的强持有属性,形成循环引用,每次进入页面创建的SignalRService实例、HubConnection实例、所有回调闭包都不会被系统回收,每秒收到消息时还会持续触发闭包持有新的dataModel对象,内存自然线性上涨。
    修复方式:所有用到外部对象的闭包都加[weak 引用对象]捕获列表,闭包内使用弱引用调用方法:
// 注册消息接收回调时,弱持有delegate避免循环引用
connection.on(method: Events.PricesUpdated.rawValue, callback: { [weak delegate] response in
    do {
        let channel = try response.getArgument(type: String.self)
        if channel == Channels.commodities.rawValue{
            if response.hasMoreArgs() {
                let dataModel : CommoditiesModel = try response.getArgument(type: CommoditiesModel.self)
                delegate?.updateComodities(dataModel: dataModel)
            }
        }else{
            if response.hasMoreArgs() {
                let dataModel : CommoditiesModel = try response.getArgument(type: CommoditiesModel.self)
                delegate?.updateIntIndecies(dataModel: dataModel)
            }
        }
    } catch {}
})

// invoke订阅方法的回调同理,弱持有self避免循环引用
connection.invoke(method: Events.Subscribe.rawValue, arguments:[Channels.commodities.rawValue,commoditiesArgument]) { [weak self] error in
    if let e = error {
        print(e)
    }else{
        print("Successfully subscribe")
    }
}
  • 断开连接时未移除注册的消息回调、未清理旧连接引用
    调用connection.stop()仅停止连接传输,不会移除之前通过on(method:)注册的回调,也不会释放HubConnection本身的强引用。需要在disconnectSignalR方法中补充清理逻辑:
    • 移除所有已注册的方法监听
    • 将connection的delegate置空
    • 在ViewController中断开连接后将connection属性也置空,确保实例完全释放
// SignalRService中断开方法补充逻辑
func disconnectSignalR(){
    connection.stop()
    // 移除所有注册的回调,方法名根据你使用的SignalR库对应API调整,常见为removeAllHandlers或者off方法
    connection.removeAllHandlers()
    connection.delegate = nil
}

// ViewController的viewDidDisappear中补充置空逻辑
override func viewDidDisappear(_ animated: Bool) {
    super.viewDidDisappear(animated)
    connection?.disconnectSignalR()
    connection = nil
}
  • 重复创建连接导致实例叠加
    didBecomeActive会在应用每次从后台返回前台、视图每次激活时触发,当前逻辑每次触发都会直接新建SignalRService实例赋值给connection属性,旧实例没有被显式断开和释放就被覆盖,会导致同时存在多个活跃连接、多组回调同时接收消息,内存占用翻倍上涨。
    修复方式:创建新连接前先判断是否已存在连接,存在则先断开清理再创建:
@objc func didBecomeActive() {
    // 先清理旧连接
    connection?.disconnectSignalR()
    connection = nil
    // 再创建新连接
    connection = SignalRService(url: URL(string: "https://myURL.com/update")!, delegate: self)
}
  • 日志级别过高导致额外内存占用
    当前配置的日志级别为.info,每秒接收消息时会输出大量日志,系统日志缓存如果没有及时释放也会占用内存,调试完成后建议将日志级别调整为.error或者直接关闭日志。

修复完成后可以用Xcode Memory Graph Debugger验证,进入退出页面多次后SignalRService和HubConnection实例数不会持续增长,内存占用会稳定在固定区间不会线性上涨。


内容的提问来源于stack exchange,提问作者Wahab Khan Jadon

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 19:33:35