SwiftUI中Combine数据流:Publisher与Subscriber绑定问题
问题核心原因
你的代码订阅写法本身没有原则性错误,测试不通过的核心原因是实例不匹配:
Game类内部自行初始化了一个Theme实例gameTheme,你的订阅逻辑监听的是这个内部实例的choosenTheme变化- 测试代码里你又新建了一个全局的
testTheme实例,所有setTheme调用都是修改这个外部实例
两个Theme实例完全独立,修改外部实例不会触发内部实例的Publisher发送事件,自然Game里的game属性不会更新。
另外代码存在一处冗余:setTheme里的空判断完全多余,参数本身就是可选类型,直接赋值即可。
修正方案
将Game依赖的Theme实例改为外部注入,保证监听对象和修改对象是同一个实例即可,修正后可运行代码如下:
import SwiftUI import Combine import Foundation class Theme: ObservableObject { @Published private(set) var choosenTheme: Color? func setTheme(with color: Color?) { // 移除冗余空判断,直接赋值 choosenTheme = color } } class Game: ObservableObject { @Published var game: String? private let gameTheme: Theme private var cancellables = Set<AnyCancellable>() // 初始化时传入需要监听的Theme实例,保证实例统一 init(theme: Theme) { self.gameTheme = theme addSubscribers() } private func addSubscribers() { gameTheme.$choosenTheme .map(createGame) .sink { [weak self] returnedString in print("Sink接收到的值: \(String(describing: returnedString))") self?.game = returnedString } .store(in: &cancellables) } private func createGame(for theme: Color?) -> String? { guard let unwrappedTheme = theme else { return nil } print(unwrappedTheme) return "\(unwrappedTheme)" } } // 测试代码 var testTheme = Theme() // 传入同一个testTheme实例给Game var testGame = Game(theme: testTheme) testTheme.setTheme(with: .orange) print(testGame.game ?? "无值") // 输出对应orange的字符串 testTheme.setTheme(with: .blue) print(testGame.game ?? "无值") // 输出对应blue的字符串 testTheme.setTheme(with: nil) print(testGame.game ?? "无值") // 输出nil
补充说明
你对Combine角色的理解基本正确:
- 加了
@Published的属性,通过$属性名访问的投影对象就是Publisher,负责发送值变化事件 sink闭包就是订阅者,负责接收Publisher发送的事件做后续处理- 订阅关系是和具体实例绑定的,只要保证你后续发值的Publisher实例,和你调用sink订阅的是同一个,就能正常收到事件。
内容的提问来源于stack exchange,提问作者Nizami
相关产品推荐
相关产品推荐

