什么是 PassthroughSubject 和 CurrentValueSubject

Posted

技术标签:

【中文标题】什么是 PassthroughSubject 和 CurrentValueSubject【英文标题】:What is PassthroughSubject & CurrentValueSubject 【发布时间】:2020-06-14 09:19:27 【问题描述】:

我碰巧研究了 Apple 新的 Combine 框架,我看到了两件事

PassthroughSubject<String, Failure>

CurrentValueSubject<String, Failure>

有人可以向我解释它们的含义和用途吗?

【问题讨论】:

你可以从这里开始Using Combine - 很有帮助。 【参考方案1】:

我认为我们可以与现实世界的案例进行类比。

PassthroughSubject = 门铃按钮

当有人敲门时,只有在家时才会通知您(您是订阅者)

PassthroughSubject 没有状态,它将接收到的任何内容发送给其订阅者。

CurrentValueSubject = 电灯开关 当你在外面时,有人会打开你家的灯。你回到家,你知道有人打开了它们。

CurrentValueSubject 有一个初始状态,它保留你放入的数据作为它的状态。

【讨论】:

这是迄今为止我见过的最好的类比。谢谢! 当大多数房子都有智能灯泡时,这个比喻可能需要更新?【参考方案2】:

PassthroughSubjectCurrentValueSubject 都是符合 Subject 协议的发布者,这意味着您可以在它们上调用 send 以随意向下游推送新值。

主要区别在于CurrentValueSubject 具有状态感(当前值),而PassthroughSubject 只是将值直接传递给其订阅者,而不记住“当前”值:

var current = CurrentValueSubject<Int, Never>(10)
var passthrough = PassthroughSubject<Int, Never>()

current.send(1)
passthrough.send(1)

current.sink(receiveValue:  print($0) )
passthrough.sink(receiveValue:  print($0) )

您会看到current.sink 立即被1 调用。 passthrough.sink 没有被调用,因为它没有当前值。只有在您订阅后发出的值才会调用接收器。

请注意,您还可以使用 CurrentValueSubjectvalue 属性获取和设置其当前值:

current.value // 1
current.value = 5 // equivalent to current.send(5)

这对于直通主题是不可能的。

【讨论】:

这不是PassthroughSubject 的好例子。您忽略了passthrough.sink(receiveValue: print($0) ) 上的可取消返回,因此即使您之后发送一些值,它也不会打印任何内容。您应该将返回值保存到变量中。 这不正确。由于所有这些都是同步运行的,因此将立即打印初始值 1。如果订阅需要更长的寿命,那么您是正确的,但是为了证明两个主题之间的差异,这非常好。 值 1 来自CurrentValueSubject。如果您在最后一行添加代码passthrough.send(90),您将永远不会打印 90。这就是我的观点。【参考方案3】:

PassthroughSubjectCurrentValueSubject 都是 Publishers——Combine 引入的一种类型——你可以订阅它们(当值可用时对值执行操作)。

它们都被设计成可以很容易地转移到使用组合范式。它们都有值和错误类型,您可以向它们“发送”值(使所有订阅者都可以使用这些值)

我看到的两者之间的主要区别是CurrentValueSubject 以一个值开头,而PassthroughSubject 不是。 PassthroughSubject 在概念上似乎更容易掌握,至少对我来说是这样。

PassthroughSubject 可以轻松用于代替委托模式,或将现有委托模式转换为组合。

//Replacing the delegate pattern
class MyType 
    let publisher: PassthroughSubject<String, Never> = PassthroughSubject()

    func doSomething() 
        //do whatever this class does

        //instead of this:
        //self.delegate?.handleValue(value)

        //do this:
        publisher.send(value)
    


//Converting delegate pattern to Combine
class MyDel: SomeTypeDelegate 
    let publisher: PassthroughSubject<String, Never> = PassthroughSubject()

    func handle(_ value: String) 
        publisher.send(value)
    

这两个示例都使用String 作为值的类型,而它可以是任何值。

希望这会有所帮助!

【讨论】:

PassthroughSubject() 是较短的版本【参考方案4】:

PassthroughSubject 用于表示事件。将它用于按钮点击等事件。

CurrentValueSubject 用于表示状态。用它来存储任何值,比如开关状态为关闭和打开。

注意:@Published 有点像CurrentValueSubject

【讨论】:

【参考方案5】:

PassthroughSubject 适用于点击动作等事件

CurrentValueSubject 适合状态

【讨论】:

以上是关于什么是 PassthroughSubject 和 CurrentValueSubject的主要内容,如果未能解决你的问题,请参考以下文章

在 SwiftUI 中观察 PassthroughSubject 中的错误

带有完成处理程序输出的 PassthroughSubject?

.send() 和 .sink() 似乎不再适用于 Xcode 11 Beta 5 中的 PassthroughSubject

合并 - 延迟发布者的发送

组合:监听内部集合变化

带有按钮和增加量的SwiftUI更新模型