SwiftUI中Combine的receiveValue代码块为何被调用两次?
为什么SwiftUI中@Published变量的sink会被多次触发?
复现代码
import SwiftUI import Combine final class ViewModel: ObservableObject { @Published var text = "" var cancellables = Set<AnyCancellable>() init() { $text .sink { completion in } receiveValue: { text in print("Text \(text)") }.store(in: &cancellables) } } struct ContentView: View { @StateObject private var vm = ViewModel() var body: some View { TextField("Title", text: $vm.text) } } #Preview { ContentView() }
触发现象
输入单个字符(比如字母A)时,控制台会重复输出两次相同内容:
Text A Text A
复杂场景下甚至会出现超过两次的调用。
根本原因
这种重复触发是SwiftUI视图机制与@Published特性共同作用的结果:
- 第一次触发:绑定直接赋值:TextField的双向绑定会直接修改
vm.text,@Published检测到属性赋值后立即发送新值,触发receiveValue第一次执行。 - 第二次触发:视图重建时的绑定同步:SwiftUI在感知到
@StateObject的@Published属性变化后,会重新计算视图的body并重建视图。这个过程中,TextField会再次同步当前的vm.text值——哪怕值没有变化,视图重建的绑定刷新逻辑仍会触发@Published发送值,导致receiveValue第二次执行。 - 多次触发的延伸场景:如果视图层级更复杂,或者存在其他依赖该
@Published属性的子视图、修饰符,每次视图更新可能带来额外的绑定同步操作,进而触发更多次的调用。
另外,@Published的设计逻辑是只要属性被赋值就发送值,不判断新值与旧值是否相同,这也是removeDuplicates()能生效的核心原因——它会过滤掉连续重复的发送值,避免无意义的重复回调。
内容的提问来源于stack exchange,提问作者Whirlwind
相关产品推荐
相关产品推荐

