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
相关产品推荐
相关产品推荐

