CurrentValueSubject 和 @Published 之间的区别

Posted

技术标签:

【中文标题】CurrentValueSubject 和 @Published 之间的区别【英文标题】:Difference between CurrentValueSubject and @Published 【发布时间】:2020-02-28 18:07:58 【问题描述】:

所以我正在研究 combine 并提出了这个问题。

使用CurrentValueSubject(并使用currentValueSubject.value 设置其值)或使用@Published var 并使用$ 访问其发布者之间有什么真正的区别吗?我的意思是我知道有人返回 Subject 而不是 Publisher,但我能发现的唯一真正区别是 CurrentValueSubject 更有用,因为您可以在协议上声明它。

我真的不明白如果我们可以使用PassthroughSubject@Published 会有什么用处,我在这里遗漏了什么吗? 请注意,这是使用 UIKit,它可能对 SwiftUI 有其他用途。

谢谢。

【问题讨论】:

【参考方案1】:

CurrentValueSubject 是一个值,一个发布者和一个订阅者合而为一。

遗憾的是,在 ObservableObject 中使用时它不会触发 objectWillChange.send()

您可以指定错误类型。

@Published 是一个属性包装器,因此:

顶层代码尚不支持。 协议声明不支持它。 它只能在一个类中使用。

@Published 在 ObservableObject 中使用时会自动触发 objectWillChange.send()

如果您尝试从后台队列发布到 @Published 包装属性,Xcode 将发出警告。可能是因为必须从主线程调用objectWillChange.send()

其发布者的错误类型为Never

我对@9​​87654329@ 最大的不满是它不能充当订阅者,并且与当前值主题相比,设置组合管道需要额外的管道。

我们可以在协议中声明@Published 属性。不是很漂亮...

protocol TestProtocol 
    var isEnabled: Bool  get 
    var isEnabledPublished: Published<Bool>  get 
    var isEnabledPublisher: Published<Bool>.Publisher  get 


class Test: ObservableObject, TestProtocol 
    @Published var isEnabled: Bool = false
    var isEnabledPublished: Published<Bool>  _isEnabled 
    var isEnabledPublisher: Published<Bool>.Publisher  $isEnabled 

【讨论】:

你不必使用@Published 你可以重新定义 var objectWillChange 成为你喜欢的任何发布者。【参考方案2】:

@Published 只是一种更简洁地使用 CurrentValueSubject 的快速方法。当我调试我的一个应用程序并查看 $paramName 返回的类型时,它实际上只是一个 CurrentValueSubject:

po self.$books
▿ Publisher
  ▿ subject : <CurrentValueSubject<Array<Book>, Never>: 0x6000034b8910>

我想使用 CurrentValueSubject 而不是 @Published 的一个好处可能是允许您使用错误类型?

注意:尽管我是 CurrentValueSubject现在我永远不会依赖这个假设。

【讨论】:

谢谢!另一个好处是能够在协议上声明它:) @Mykod 有趣,我没有考虑过协议,是的,像 @Published 这样的包装器是不允许的:D 这是可能的,但它很丑……看我的回答。【参考方案3】:

我发现自己又回到了这篇文章,所以我想对@PublishedCurrentValueSubject 之间的区别添加一些额外的见解。

可以在@Published 的文档中找到一个主要区别:

当属性发生变化时,会在属性的 willSet 块中进行发布,这意味着订阅者会在新值被实际设置在属性上之前收到它。

此外,Swift Forums 上的对话请注意,@Published 旨在与 SwiftUI 一起使用。

关于@Published 在其属性的willSet 块中发布,请考虑以下示例:

class PublishedModel 
    @Published var number: Int = 0


let pModel = PublishedModel()

pModel.$number.sink  number in
    print("Closure: \(number)")
    print("Object:  \(pModel.number) [read via closure]")


pModel.number = 1
print("Object:  \(pModel.number) [read after assignment]")

这会产生以下输出:

Closure: 0
Object:  0 [read via closure]
Closure: 1
Object:  0 [read via closure]
Object:  1 [read after assignment]

将此与另一个示例进行对比,在该示例中,除了将 @Published 替换为 CurrentValueSubject 之外,我们保持相同:

class CurrentValueSubjectModel 
    var number: CurrentValueSubject<Int, Never> = .init(0)


let cvsModel = CurrentValueSubjectModel()

cvsModel.number.sink  number in
    print("Closure: \(number)")
    print("Object:  \(cvsModel.number.value) [read via closure]")


cvsModel.number.send(1)

print("Object:  \(cvsModel.number.value) [read after assignment]")

输出:

Closure: 0
Object:  0 [read via closure]
Closure: 1
Object:  1 [read via closure] // <— Here is the difference
Object:  1 [read after assignment]

number 更新为1 后,读取对象的CurrentValueSubject 的值属性 闭包打印新值而不是旧值,就像@Published 一样强>。

总之,在您的ObservableObjects 中使用@Published 来获取您的SwiftUI 视图。如果您希望创建某种模型对象,其实例属性包含当前值并且也在设置后发布它的更改,请使用CurrentValueSubject

【讨论】:

【参考方案4】:

@Published 的一个优点是它可以充当私有可变、公共不可变的 CurrrentValueSubject。

比较:

@Published private(set) var text = "someText"

与:

let text = CurrentValueSubject<String, Never>("someText")

在设计 API 时,您通常希望允许客户端读取当前值并订阅更新,但阻止它们直接设置值。

【讨论】:

以上是关于CurrentValueSubject 和 @Published 之间的区别的主要内容,如果未能解决你的问题,请参考以下文章

从另一个 CurrentValueSubject 创建一个 CurrentValueSubject

Combine - 测试 CurrentValueSubject 是不是有订阅者

如何使用 Combine 的 CurrentValueSubject 并在 SwiftUI 视图中访问它?

带有 CurrentValueSubject 绑定的 TextField 上的“Binding<String> 操作尝试每帧更新多次”

广州千必胜解密2016最新普通pu克分析仪原理

X_PU