了解Combine中的share()

Tik*_*der 7 swift combine

众所周知,通常发布者是结构体。如果是一个类会发生什么变化?

假设我们有 1 个发布者发出 1 个值和 2 个订阅者订阅它。

let p1 = Just(20)
let s1 = p1.print().sink { _ in }
let s2 = p1.print().sink { _ in }

// s1 - receive value: (20)
// s2 - receive value: (20)
Run Code Online (Sandbox Code Playgroud)

在打印日志中,我们可以看到两个订阅者都获得了值 (20)。

如果我们打开 share() 操作符的文档,我们会看到

share() - 将发布者作为类实例返回。

所以它只是将发布者的语义从值更改为引用。在我们的示例中,我们不将p1发布者传递给任何函数或分配给任何对象,这就是为什么对我来说发布者是结构体或类没有区别的原因......但是如果我添加share()操作符行为会有所不同,s2不会获得价值.

let p1 = Just(20).share() // !
let s1 = p1.print().sink { _ in }
let s2 = p1.print().sink { _ in }

// s1 - receive value: (20)
Run Code Online (Sandbox Code Playgroud)

我看到了一些带有URLSession.shared.dataTaskPublisher(\_: URL)或带有一些“延迟”发布者的例子,当它们s2也获得价值时,但我仍然不清楚仅仅改变发布者的语义是如何以这种方式改变其行为的。

mat*_*att 13

The problem is that you are not using a pipeline where it does make a difference. Consider this example (based on a Cocoa With Love article) where a second subscriber comes online after the publisher has been publishing for some time:

let pub1 = Timer.publish(every: 1, on: .main, in: .default)
let c1 = pub1.connect()
let scan = Publishers.Scan(upstream: pub1, initialResult: 0) { (a, b) -> Int in
    a + 1
}
scan.sink { print("a:", $0) }.store(in:&storage)
delay(3) {
    scan.sink { print("b:", $0) }.store(in:&self.storage)
}
Run Code Online (Sandbox Code Playgroud)

The point is, there is only one scan and it is producing 1, 2, 3 when after a delay another subscriber comes along. What will that subscriber get? Will it just pick up where we are now? No. We get this:

a: 1
a: 2
a: 3
a: 4
b: 1
a: 5
b: 2
a: 6
b: 3
...
Run Code Online (Sandbox Code Playgroud)

So in effect we start all over again with our second subscription, because the publisher is a new copy. But if we promote the publisher to a class, we get completely different results:

let pub1 = Timer.publish(every: 1, on: .main, in: .default)
let c1 = pub1.connect()
let scan = Publishers.Scan(upstream: pub1, initialResult: 0) { (a, b) -> Int in
    a + 1
}
let scan2 = scan.share() // <--
scan2.sink { print("a:", $0) }.store(in:&storage)
delay(3) {
    scan2.sink { print("b:", $0) }.store(in:&self.storage)
}
Run Code Online (Sandbox Code Playgroud)

Now we get this:

a: 1
a: 2
a: 3
a: 4
b: 4
a: 5
b: 5
a: 6
b: 6
a: 7
b: 7
Run Code Online (Sandbox Code Playgroud)

Obviously that's a very significant difference. You can see the same sort of thing if your publisher is a Subject, because that's a class, not a struct.