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

iOS Combine中flatMap内用.delay导致数据丢失问题求助

问题原因分析

你遇到的问题不是背压问题,核心原因是重复点击按钮时,旧的订阅没有被取消,多个订阅实例同时修改同一个content变量,导致字符混乱、丢失:

  • 每次点击Test按钮都会调用test(),创建一个新的Combine订阅并添加到cancellableSet中,但之前的订阅并没有被移除——这些旧订阅的延迟任务还在队列中等待执行,会继续向content追加字符。
  • 当你重置content = ""时,旧订阅后续发射的字符会和新订阅的字符混在一起写入content,最终导致内容错乱、长度不匹配。
解决方案

解决这个问题的关键是每次创建新订阅前,取消所有旧订阅,确保同一时间只有一个订阅在修改content。有两种常见实现方式:

方式1:清空订阅集合

在test()开头先清空cancellableSet,销毁所有旧订阅:

func test() {
    cancellableSet.removeAll() // 先取消所有旧订阅
    content = ""
    
    aiRepsonse.publisher
        .flatMap{
            return Just($0).delay(for: 0.01, scheduler: RunLoop.main)}
        .sink { _ in
            if content.count != aiRepsonse.count{
                print("error! lost data! content.count is \(content.count),  aiRepsonse.count is \( aiRepsonse.count)")
                print("content    is : \(content)")
                print("aiRepsonse is : \(aiRepsonse)")
            }

        } receiveValue: { value in
            content += String(value)
        }
        .store(in: &cancellableSet)
}

方式2:使用单个Optional订阅对象

如果只需要同时存在一个订阅,可以把cancellableSet替换为单个AnyCancellable?变量,每次新订阅会自动替换旧订阅,旧订阅会被销毁:

struct ContentView: View {
let aiRepsonse = "View the latest news and breaking news today for U.S., world, weather, entertainment, politics and health at abc.com."

@State private var cancellable: AnyCancellable? // 单个可选订阅对象
@State private var content = ""

func test() {
    cancellable = nil // 取消旧订阅
    content = ""
    
    cancellable = aiRepsonse.publisher
        .flatMap{
            return Just($0).delay(for: 0.01, scheduler: RunLoop.main)}
        .sink { _ in
            if content.count != aiRepsonse.count{
                print("error! lost data! content.count is \(content.count),  aiRepsonse.count is \( aiRepsonse.count)")
                print("content    is : \(content)")
                print("aiRepsonse is : \(aiRepsonse)")
            }

        } receiveValue: { value in
            content += String(value)
        }
}

var body: some View {
    VStack {
        Text(content)
            .frame(height: 200)
        Button(action: {
            test()
        }, label: {
            Text("Test")
        })
    }
}
}

额外优化:限制flatMap并发数

虽然不是问题根源,但你的flatMap没有限制并发数,会一次性创建所有字符的延迟任务。可以通过maxPublishers: .max(1)让字符按顺序发射,避免并发执行的潜在问题:

.flatMap(maxPublishers: .max(1)) {
    Just($0).delay(for: 0.01, scheduler: RunLoop.main)
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 04:33:21