Combine:订阅、绑定和内存管理

我正在参加「掘金·启航计划」

sink

sink负责订阅Publisher,并返回一个AnyCancellabel

完整签名:

 public func sink(receiveCompletion: @escaping ((Subscribers.Completion<Self.Failure>) -> Void), receiveValue: @escaping ((Self.Output) -> Void)) -> AnyCancellable

它接受两个闭包回调做为参数,receiveCompletion 在发布结束事件时被调用,receiveValue 在发布值时被调用。

来自喵神《SwiftUI和Combine编程》中的发布-订阅流程图:

iShot_2023-04-28_17.35.40.png

在使用 sink 完成订阅时,会创建一个特殊的 Subscriber 类型 Subscribers.Sink,并被纳入上面的流程中。它会自动声明想要接收无限多个新值,并订阅相应的 Publisher 对象。接下来,Publisher 会将新的值和结束事件作为参数传递给 sink 传入的两个闭包,从而将响应式的事件流转化为普通的指令操作。

Backpressure(背压)

Subscriber 可以在订阅初期通过 Subscription.request 或者通过 Subscriber.receive 返回特定的 Subscribers.Demand 值来指定能够处理的值的个数。这种背压机制 (Backpressure),可以让我们指定合适的背压策略,来控制可接收的值的上限,防止出现上游的发布速度超过下游消费速度的问题。

assign

Combine内建的另一个Subscriber就是assign,它可以用来将 Publisher 的输出值通过 key path 绑定到一个对象的属性上去。

使用assign时有几点需要注意的地方:

  1. 只有 class 上用 var 声明的属性才可以通过 assign 来直接赋值

  2. 上游 Publisher 的 Failure 的类型必须是 Never。如果上游 Publisher 可能会发生错误,必须先对它进行处理,比如使用 replaceError或者 catch 来把错误在绑定之前就处理掉。

  3. 核心概念 中已经有过说明,使用 assign(to:on:) 并存储生成的 AnyCancellable,可能会引起引用循环,所以请尽量使用assign(to:)来替代assign(to:on:)

引用共享

一个Publisher可能会有多个Subscriber,如果这个Publisher是一个网络请求的话,由于 dataTaskPublisher 是Struct,遵循值语义,多次订阅会复制多份,每一份都是一个新的Publisher,而这会造成多次请求,这是很浪费资源的。

解决上面问题的方法就是使用share()来共享Publisher。share()操作会把原来的Publisher包装到class内,对它的进一步变形也会适用于引用语义。

Cancellable & AnyCancellable

使用sink或者assign订阅Publisher时,会返回一个类型为AnyCancellable类型的值;而Timer在执行connect()操作后得到的是一个遵循Cancellable协议的值。

对于 Cancellable 来说,需要在合适的时候主动调用 cancel() 方法来完结。如果在没有调用 cancel() 的情况下就将 connect 的返回值忽略或者释放掉,那么Timer会一直计时,永远不会被终结掉。所以对于需要connect的Publisher,需要显式的调用cancel()来结束事件。

AnyCancellable 是一个 class,它可以对自身的生命周期进行管理。在 AnyCancellable 被释放时,它对应的订阅操作也会停止。

在应用中,我们会在实例当中创建一个Set<AnyCancellable>存储属性,并将sink或者assign返回的AnyCancellable存储其中。这样,当该实例 deinit 时,AnyCancellable 的 deinit 也会触发,并自动释放资源。这跟 RxSwift 中的 DisposeBag 很类似。

参考

王巍 《SwiftUI和Combine编程》

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYRKD530' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片