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

如何解决Combine中AsyncPublisher的内存泄漏问题

异步遍历AsyncPublisher引发内存泄漏的问题及解决

问题描述

使用AsyncPublisher(异步序列)遍历定时器序列时,即便在Task中通过[weak self]弱引用捕获实例,将类对象置为nil后,实例仍无法被释放,定时器持续输出时间戳,存在内存泄漏。

相关代码如下:

import Foundation
import Combine

class MyServiceClass {
    
    let timerSequence = Timer.publish(every: 1, on: .main, in: .default).autoconnect().values
    
    init() {
        Task { [weak self] in
            await self?.setUpStreamIteration()
        }
    }
    
    func setUpStreamIteration() async {
        for await time in timerSequence {
            print(time)
        }
    }
}

var service: MyServiceClass? = MyServiceClass()
service = nil

运行后控制台持续输出时间戳,说明MyServiceClass实例未被正常释放。

问题原因

核心问题在于**for await...in遍历异步序列时会强引用序列本身**:

  • timerSequence是MyServiceClass的实例属性,类实例持有该序列;
  • 遍历操作强引用timerSequence,而序列因定时器持续运行保持活跃,间接“留住”了类实例,形成隐性循环引用;
  • 虽然Task用[weak self]捕获,但进入setUpStreamIteration方法后,遍历对序列的强引用已形成,弱引用无法打破这个循环。

解决方案

方案1:保存遍历Task并在deinit中取消

通过保存用于遍历的Task实例,在类的deinit方法中取消该Task,终止遍历操作,释放对序列的引用,打破循环:

import Foundation
import Combine

class MyServiceClass {
    
    let timerSequence = Timer.publish(every: 1, on: .main, in: .default).autoconnect().values
    private var iterationTask: Task<Void, Never>?
    
    init() {
        iterationTask = Task { [weak self] in
            await self?.setUpStreamIteration()
        }
    }
    
    func setUpStreamIteration() async {
        // Task被取消时,遍历循环会自动终止
        for await time in timerSequence {
            print(time)
        }
    }
    
    deinit {
        iterationTask?.cancel()
        print("MyServiceClass 已释放")
    }
}

var service: MyServiceClass? = MyServiceClass()
service = nil

方案2:在遍历中显式处理Task取消(可选)

可以在遍历方法中捕获Task取消错误,让逻辑更清晰:

func setUpStreamIteration() async {
    do {
        // Task被取消时会抛出CancellationError
        for try await time in timerSequence.values {
            print(time)
        }
    } catch is CancellationError {
        print("遍历已取消")
    } catch {
        print("遍历出错:\(error)")
    }
}

验证效果

修改后将service置为nil,控制台会输出MyServiceClass 已释放,且时间戳停止输出,说明实例成功释放,内存泄漏问题解决。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 18:22:42