【问题标题】:What's the difference between .sink and Subscribers.Sink?.sink 和 Subscribers.Sink 有什么区别?
【发布时间】:2020-04-14 20:41:33
【问题描述】:

我想用 Future 做一个异步工作。 但是下面的 .sink() 闭包 永远不会被调用。 看来 Future 的实例是在被调用后立即释放的。

    Future<Int, Never> { promise in
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            promise(.success(1))
        }
    }
    .receive(on: DispatchQueue.main)
    .sink(receiveCompletion: { completion in
        print(completion)
    }, receiveValue: {
        print($0)
    })

所以我将 .sink() 闭包 替换为 .subscribe(Subscribers.Sink()),如下所示。它工作正常。 但问题是我不明白为什么它可以正常工作。 :( 在我看来是一样的。 这两个代码有什么区别?什么时候可以使用 .sink(),什么时候不可以?

    Future<Int, Never> { promise in
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            promise(.success(1))
        }
    }
    .receive(on: DispatchQueue.main)
    .subscribe(Subscribers.Sink(receiveCompletion: { completion in
        print(completion)
    }, receiveValue: {
        print($0)
    }))

提前致谢。

【问题讨论】:

  • 有趣的问题!我不知道答案,但我可以告诉你代码是mass noun,所以这些不称为“代码”。相反,它们是“代码片段”。 ????‍♂️

标签: swift swiftui combine


【解决方案1】:

.sink 运算符做了三件事:

  • 它使用您传递给它的两个闭包创建一个Subscribers.Sink
  • 它在上游Publisher 上调用subscribe,传递它创建的Sink
  • 它会创建一个AnyCancellable,当它被销毁时,会取消Sink。它返回对此AnyCancellable 的引用。

AnyCancellable 是一个引用计数对象。当对AnyCancellable 的最后一个引用被销毁时,AnyCancellable 本身也被销毁。那时它调用了自己的cancel方法。

在您的第一个示例中,您没有保存.sink 返回的AnyCancellable。所以 Swift 会立即销毁它,这意味着它会立即取消订阅。一秒后,你的asyncAfter闭包调用promise,但订阅已经取消,所以你的receiveValue闭包没有被调用。

在您的第二个示例中,由于您正在创建 Subscribers.Sink 对象并将其传递给自己的 subscribe,因此不会创建 AnyCancellable 来包装 Sink。所以没有什么会自动销毁订阅。一秒钟后,asyncAfter 闭包调用promise。由于订阅没有被销毁,它仍然存在,所以你的receiveValue闭包被调用,然后你的receiveCompletion闭包被调用。

所以这实际上是一个非常有趣的Subscribers.Sink 用法,而不是.sink 运算符。使用.sink,您必须保存返回的AnyCancellable,否则订阅将立即取消。但是通过直接使用Subscribers.Sink,您可以创建一个持续到完成的订阅,并且您无需保存任何内容。当订阅完成时(使用.finished.failure),Sink 会丢弃Subscription,这会破坏保持它处于活动状态的保留周期,因此SinkSubscription 也是销毁,不留内存泄漏。

【讨论】:

  • 有趣,所以它使用了一个有意的强保留周期来保持订阅和接收器都处于活动状态,直到取消事件到达?是否有外部方法使其停止?
  • Subscribers.Sink 符合 Cancellable。如果将Sink保存在自己的属性中,可以直接调用它的cancel方法。
  • 哦,太好了!感谢分享
  • 有没有办法提取传递给AnyPublisherSubscribers.Sink 对象?因为如果我必须将Subscribers.Sink 保存到外部类属性以从其他地方调用cancel(),那么这与调用接收器并将 AnyCancelable 保存为类属性返回是一样的。
  • 不,没有办法让订阅者脱离 SDK 中提供的AnyPublisher 或任何其他Publisher。如果你想取消订阅,你应该使用.sink
猜你喜欢
  • 2018-10-25
  • 2019-08-02
  • 2021-10-17
  • 2010-10-02
  • 2011-12-12
  • 2010-09-16
  • 2012-03-14
  • 2012-02-06
  • 2011-02-25
相关资源
最近更新 更多