我有一个类似于下面的Playground示例的发布者设置。
import Combine
let sub1 = PassthroughSubject<String, Never>().prepend("initial 1")//.share()
let sub2 = PassthroughSubject<String, Never>().prepend("initial 2")
Publishers.CombineLatest(sub1, sub2).sink { content1, content2 in
print("combined received: \(content1) and \(content2)")
}
sub1.sink { content1 in
print("first received: \(content1)")
}
sub2.sink { content2 in
print("second received: \(content2)")
}
如果注解掉sub1
后面的share()
,则控制台将打印以下内容:
combined received: initial 1 and initial 2
first received: initial 1
second received: initial 2
这似乎是意料之中的。但如果share()
存在,则会产生意外的打印:
combined received: initial 1 and initial 2
second received: initial 2
在我的XCode项目中,我使用prepend()
来触发初始执行,但是share()
阻止了一些链的初始执行。
1条答案
按热度按时间lo8azlld1#
是的,这是意料之中的。当使用
share
时,基本上将发布者更改为引用类型语义。sub1
只有一个示例,一旦使用sink
订阅它,它就会同步发布您预先添加的第一个元素(prepend
是同步的),结果是您后来添加的订阅者 * 不会 * 收到您预先添加的元素。另一方面,当你不使用
share
的时候,它就完全是值语义了,你可以把它看作是sink
在发布者的副本上添加了一个订阅者,就像复制值类型一样,然后每个副本分别发布自己的“初始值1”。在
share
的documentation中,您可以看到他们通过添加延迟来解决“share
将所有元素同步发布到第一个订阅者”的问题:以下示例使用序列发布器作为计数器来发布由
map(_:)
运算符生成的三个随机数。它使用share()
运算符来为两个订阅者中的每一个共享相同的随机数。此示例仅使用delay(for:tolerance:scheduler:options:)
运算符来防止第一个订阅者立即用尽序列发布器;异步发布者不需要这个。您也可以在示例中执行此操作:
您将看到“第一个收到”行被打印出来。
基本上,发布者现在
share
将其元素与其所有订阅者关联起来,如果您希望所有订阅者都有一个初始元素,则需要确定 * 何时 * 添加所有订阅者(在上面的例子中,在两个sink
之后),并且只有在那个点之后才能发布“初始元素”(等待0.1秒是远远超过这一点)。否则,一些订户不会接收到该元素。