【问题标题】:.send() and .sink() do not seem to work anymore for PassthroughSubject in Xcode 11 Beta 5.send() 和 .sink() 似乎不再适用于 Xcode 11 Beta 5 中的 PassthroughSubject
【发布时间】:2019-12-07 19:09:03
【问题描述】:

在下面的代码中,当按下按钮时,应该在控制台中打印“Test”,但事实并非如此。该事件不通过发布者发送。 知道 Xcode 11 Beta 5 中的 PassthroughSubject 发生了什么吗? (在 Xcode 11 Beta 4 中运行良好)

var body: some View {  

    let publisher = PassthroughSubject<String, Never>()

    publisher.sink { (str) in  
        print(str)  
    }  
    return Button("OK") {  
        publisher.send("Test")  
    }  
}

附:我知道按下按钮时还有其他方法可以打印字符串,我只想展示一个简单的发送接收示例

【问题讨论】:

    标签: swiftui combine


    【解决方案1】:

    .sink() 返回一个AnyCancellable 对象。你永远不应该忽视它。 永远不要这样做

    // never do this!
    publisher.sink { ... }
    
    // never do this!
    let _ = publisher.sink { ... }
    

    如果将其分配给变量,请确保它不是短命的。一旦可取消对象被释放,订阅也会被取消。

    // if cancellable is deallocated, the subscription will get cancelled
    let cancellable = publisher.sink { ... }
    

    既然您要求在视图中使用sink,我将发布一种方法。但是,在视图中,您可能应该改用.onReceive()。它更简单。

    使用接收器:

    在视图中使用它时,您需要使用@State 变量,以确保它在生成视图主体后仍然存在。

    DispatchQueue.main.async 是必需的,以避免在视图更新时修改状态。否则会出现运行时错误。

    struct ContentView: View {
        @State var cancellable: AnyCancellable? = nil
    
        var body: some View {
            let publisher = PassthroughSubject<String, Never>()
    
            DispatchQueue.main.async {
                self.cancellable = publisher.sink { (str) in
                    print(str)
                }
            }
    
            return Button("OK") {
                publisher.send("Test")
            }
        }
    }
    

    使用.onReceive()

    struct ContentView: View {
    
        var body: some View {
            let publisher = PassthroughSubject<String, Never>()
    
            return Button("OK") {
                publisher.send("Test")
            }
            .onReceive(publisher) { str in
                print(str)
            }
        }
    }
    

    【讨论】:

    • 为了解除订阅,我使用了这样的东西:self.cancellable = publisher.sink(receiveCompletion: { (completion) in self.cancellable = nil }, receiveValue: { (str) in print(str) })。这是正确的解决方案还是有更好的解决方案?
    • 我不是Combine方面的专家,但它似乎是正确的;-)
    • 作为一个 Combine 和响应式初学者,对于非常简单的任务,周围的许多示例似乎过于复杂:struct ContentView: View { @State var string: String = "" { didSet { print(string) } } var body: some View { return Button("OK") { self.string = "Test" } } } 抱歉内联代码。为什么我们要使用主题?我猜是因为它开辟了使用运算符等的机会?
    【解决方案2】:

    订阅接收器时缺少 .store。 您可以使用 .onReceive,但您的代码没有接收到值,因为您需要添加 .store(in: &subscription)

    var body: some View {  
        var subscription = Set<AnyCancellable>()
        let publisher = PassthroughSubject<String, Never>()
    
        publisher.sink { (str) in  
            print(str)  
        }.store(in: &subscription)
    
        return Button("OK") {  
            publisher.send("Test")  
        }  
    }
    

    【讨论】:

      猜你喜欢
      • 2020-03-29
      • 2019-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-28
      • 1970-01-01
      • 1970-01-01
      • 2019-12-19
      相关资源
      最近更新 更多