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

Swift Combine使用CombineLatest引发内存泄漏问题求助

内存泄漏原因分析与解决方法

问题根源

你的内存泄漏完全是循环引用导致的,咱们来拆解一下这个闭环:

在第一段代码的map闭包中,你直接访问了self.channelId:

.map { $0.chatState.messagesByChannel[self.channelId] }

这个闭包没有用[weak self]修饰,会强引用视图控制器实例。而store.$state是Published属性,它的Publisher会长期持有这个map闭包(只要订阅存在)。同时,视图控制器持有cancellableSet,这个集合又持有sink的订阅对象,订阅对象又依赖由store.$state生成的messages Publisher——这就形成了无法打破的循环链:
视图控制器 → cancellableSet → 订阅 → messages Publisher → map闭包 → 视图控制器

反观第二段代码,map闭包只访问了state.chatState.getChatRoomDetailResponse,没有捕获任何self的引用,所以不存在循环,视图控制器能正常释放,deinit也就会被调用。

解决方法

有两种简单的方式可以打破这个循环:

方法1:提前捕获channelId为局部变量

把channelId先提取到闭包外的局部变量中,让map闭包捕获这个变量而非self:

let messages = {
    // 提前保存channelId,避免闭包捕获self
    let targetChannelId = self.channelId
    return store.$state
        .map { $0.chatState.messagesByChannel[targetChannelId] }
        .removeDuplicates()
        .eraseToAnyPublisher()
}()

方法2:在map闭包中使用弱引用self

如果需要在闭包中访问更多self的属性,可以给map闭包加上[weak self]修饰,并做可选绑定:

let messages = {
    store.$state
        .map { [weak self] state in
            guard let self = self else { return nil }
            return state.chatState.messagesByChannel[self.channelId]
        }
        .removeDuplicates()
        .eraseToAnyPublisher()
}()

两种方法都能打破循环引用,让视图控制器在销毁时正常触发deinit,解决内存泄漏问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 10:32:35