4

我遇到了一些组合问题,在https://developer.apple.com/documentation/combine/publisher中遇到了“与多个订阅者一起工作”部分:

func multicast<S>(() -> S) -> Publishers.Multicast<Self, S>

func multicast<S>(subject: S) -> Publishers.Multicast<Self, S>

但是,当我尝试确认发送给多个订阅者时需要多播的假设时,我发现在尝试此操场代码时不需要这样做(修改自https://github.com/AvdLee/CombineSwiftPlayground/blob/ master/Combine.playground/Pages/Combining%20Publishers.xcplaygroundpage/Contents.swift)(在 Xcode 版本 11.0 beta 3 (11M362v) 中的 10.14.5 上运行):

enum FormError: Error { }

let usernamePublisher = PassthroughSubject<String, FormError>()
let passwordPublisher = PassthroughSubject<String, FormError>()

let validatedCredentials = Publishers.CombineLatest(usernamePublisher, passwordPublisher)
    .map { (username, password) -> (String, String) in
        return (username, password)
    }
    .map { (username, password) -> Bool in
        !username.isEmpty && !password.isEmpty && password.count > 12
    }
    .eraseToAnyPublisher()

let firstSubscriber = validatedCredentials.sink { (valid) in
    print("First Subscriber: CombineLatest: Are the credentials valid: \(valid)")
}

let secondSubscriber = validatedCredentials.sink { (valid) in
    print("Second Subscriber: CombineLatest: Are the credentials valid: \(valid)")
}

// Nothing will be printed yet as `CombineLatest` requires both publishers to have send at least one value.
usernamePublisher.send("avanderlee")
passwordPublisher.send("weakpass")
passwordPublisher.send("verystrongpassword")

这打印:

First Subscriber: CombineLatest: Are the credentials valid: false
Second Subscriber: CombineLatest: Are the credentials valid: false
First Subscriber: CombineLatest: Are the credentials valid: true
Second Subscriber: CombineLatest: Are the credentials valid: true

所以似乎不需要多播来解决多个订阅者。还是我错了?

那么,这些多播功能是做什么用的,我将如何使用它们呢?一些示例代码会很好。

谢谢,

拉斯

4

2 回答 2

4

PassthroughSubject 不是一个很好的测试示例,因为它是一个类并为您提供参考语义。因此,在一个简单的情况下,两个订阅者可以直接订阅它,并在主题发出一个值时同时接收相同的值。

但这里有一个更好的测试用例(灵感来自关于Cocoa With Love的讨论):

    let pub1 = Timer.publish(every: 1, on: .main, in: .default).autoconnect()
    let sub = CurrentValueSubject<Int,Never>(0)
    let scan = sub.scan(10) {i,j in i+j}
    pub1.sink { _ in let i = sub.value; sub.value = i+1 }.store(in:&storage)
    scan.sink { print("a", $0) }.store(in:&storage)
    delay(3) {
        scan.sink { print("b", $0) }.store(in:&self.storage)
    }

sink当第二个作为该管道的新订阅者出现时,这给出了一个非常奇怪的结果:

a 10
a 11
a 13
a 16
b 13
a 20
b 17
a 25
b 22
a 31
b 28
a 38
b 35

下沉a并且b彼此得到一系列不同的数字,实际上是因为scan它是一个结构。如果我们希望它们获得相同的数字,我们可以使用多播:

    let scan = sub.scan(10) {i,j in i+j}.multicast {PassthroughSubject()}.autoconnect()

这会产生

a 10
a 11
a 13
a 16
a 20
b 20
a 25
b 25

这是连贯的。

但是,这仍然不能证明您需要multicast,因为您可以通过说.share()来完成同样的事情。multicast我不清楚和之间有什么区别share

于 2019-12-17T02:25:08.357 回答
0

来自 swift 论坛的回答/参考暗示多播方法旨在建立在 .share() 运算符的基础上。来自菲利普的帖子

在这种情况下,它用于将上游连接到 PassthroughSubject,然后自动连接。通常,当订阅者收到订阅时,它将取消第一个订阅之后的任何其他订阅,多播为此行为提供了一个逃生舱口并处理多个订阅。

在实践中,如果您想在 Combine 中通过多个管道拆分流和多播事件更新,似乎最实用的方法是创建一个 @Published 属性,让任何上游管道使用 .assign() 或在 .sink( ),然后使用来自 @Published 属性的订阅者设置其他管道。

于 2019-07-18T21:41:44.770 回答