CurrentValueSubject 和@Published 之间的区别

Difference between CurrentValueSubject and @Published

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

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

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

谢谢。

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

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

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

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

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

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

您可以指定错误类型。

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

  • 顶级代码尚不支持。
  • 协议声明不支持它。
  • 只能在class内使用。

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

如果您尝试从后台队列发布到 @Published 包装 属性,

Xcode 将发出警告。可能是因为 objectWillChange.send() 必须从主线程调用。

其发布者的错误类型是Never

我最反对 @Published 的是它不能充当订阅者,并且与当前值主题相比,设置 Combine 管道需要额外的管道。

我们可以在协议中声明一个@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 的一个优点是它可以充当 private-mutable、public-immutable CurrrentValueSubject。

比较:

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

与:

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

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

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

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

When the property changes, publishing occurs in the property’s willSet block, meaning subscribers receive the new value before it’s actually set on the property.

此外,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.

使用@Published 时有一项限制。

您只能在 Class 的属性上使用 @Published,而 CurrentValueSubject 也可以用于结构