当前位置: 首页 > news >正文

Swift Combine — Subject Publishers(PassthroughSubject CurrentValueSubject)

本文主要介绍一下SubjectSubject 本身也是一个 Publisher,其定义如下:

public protocol Subject<Output, Failure> : AnyObject, Publisher {func send(_ value: Self.Output)func send(completion: Subscribers.Completion<Self.Failure>)func send(subscription: any Subscription)
}

从定义可以看到,Subject 暴露了三个 send 方法,外部调用者可以通过这些方法来主动地发布 output 值、failure 事件、 finished 事件以及subscription

Combine 内置提供了两种常用的 Subject 类型,分别是 PassthroughSubjectCurrentValueSubject,下面来分别看一下。

PassthroughSubject

PassthroughSubjectCombine框架中的一种Subject具体类型,它不持有任何值,将自己接收到的任何值简单的传递给下游的Subscriber

当我们要创建一个PassthroughSubject时,需要指定要发送的值的类型,然后使用send方法发送,任何的Subscriber都会收到这个值。因为它本身不持有值,所以如果下游没有Subscriber,那么这个值将废弃了。

  func testPassthroughSubjectPublisher() {let publish2 = PassthroughSubject<String, Error>()publish2.send("1")publish2.sink { completion inswitch completion {case .finished:print("---> Finished")case .failure(let error):print("---> Error: \(error.localizedDescription)")}} receiveValue: { value inprint("---> value is: \(value)")}.store(in: &cancellable)publish2.send("2")publish2.send(completion: .finished)}

上面代码中创建了PassthroughSubject实例publish2,并通过sink方法添加了订阅者,订阅后,随后通过send方法发送了2.finished事件,sink方法中对应的输出都正常。但是在添加sink方法之前发送的send("1")没有任何输出,就像上面说的,发送的时候还没有任何订阅者,发送的值就直接抛弃了。

输出结果:

---> value is: 2
---> Finished

作为Subject的具体实现,PassthroughSubject提供了一种方便的方法,使现有的命令式代码适应Combine模型。

CurrentValueSubject

CurrentvaluessubjectCombine框架中的一种Subject具体类型。它可以保存单个值,并在设置新值时向任何订阅者发布新值。
Currentvaluessubject在初始化的时候需要设置一个初始值。

let publisher = CurrentValueSubject<String, Error>("one")

当有订阅者订阅的时候会立即发送这个值。下面代码中当初始化CurrentValueSubjectViewModel的时候,则会直接输出“—> value is: one”

class CurrentValueSubjectViewModel: ObservableObject {init() {setUpPublisher()}func setUpPublisher() {let publisher = CurrentValueSubject<String, Error>("one")let cancelable = publisher.sink { completion inswitch completion {case .finished:print("---> Finished")case .failure(let error):print("---> Error: \(error.localizedDescription)")}} receiveValue: { value inprint("---> value is: \(value)")}}
}

下面代码中,在viewModel中实例化了一个CurrentValueSubject,并添加了subscriber,在SwiftUI界面添加了三个按钮,用来发送数据。

在发送数据的时候,可以通过send方法,也可以通过直接设置value的方法,效果都是一样的。

class CurrentValueSubjectViewModel: ObservableObject {private var cancellable = Set<AnyCancellable>()let publisher = CurrentValueSubject<String, Error>("one")init() {setUpPublisher()}func setUpPublisher() {publisher.sink { completion inswitch completion {case .finished:print("---> Finished")case .failure(let error):print("---> Error: \(error.localizedDescription)")}} receiveValue: { value inprint("---> value is: \(value)")}.store(in: &cancellable)}func sendMessage() {publisher.send("Hello World")publisher.value = "Swift Combine"}func sendError() {publisher.send(completion: .failure(NetworkError.invalidURL))}func sendFinished() {publisher.send(completion: .finished)}}struct CurrentValueSubjectDemo: View {@StateObject private var viewModel = CurrentValueSubjectViewModel()var body: some View {VStack {Button("Send Message") {viewModel.sendMessage()}Button("Send Finished") {viewModel.sendFinished()}Button("Send Error") {viewModel.sendError()}}.buttonStyle(BorderedProminentButtonStyle())}
}

当点击按钮时,输出如下:
在这里插入图片描述

关于Subject生命周期

Subject是有生命周期的,放发送了completion后(不管是finished还是error),Subject都不会再发送任何新值。

就上面的CurrentValueSubject为例,在发送一个value之后,就发送finished,然后在发送value就无效了。
在这里插入图片描述
PassthroughSubject亦是如此。所以当使用PassthroughSubjectCurrentValueSubject时,重要的是要考虑生命周期,并在明显没有任何值发送时关闭Subject

PassthroughSubject与CurrentValueSubject区别

首先这两个都是Subject的具体实现,都可以根据需要异步地无限地发出事件。这两个Subject的用法都比较简单,都作为Publisher发布数据,不过却别还是有的。

PassthroughSubject没有初始值,也不需要持有最近一次发布的值。
CurrentValueSubject可以为Publisher提供初始值,并通过更新 value属性自动发出事件。

网上有一个较为恰当的比喻:
PassthroughSubject就像一个门铃按钮。当有人按门铃时,只有当你在家时才会通知你。
CurrentValueSubject就像一个电灯开关。当你不在的时候灯是开着的,当你回家的时候你仍然会注意到它是开着的。

写在最后

本文主要介绍了PassthroughSubjectCurrentValueSubject的概念、使用以及一些区别,希望大家通过本文能对这两个Subject有个初步的了解和使用,文中如果有不对的地方,还望大家指正。

最后,希望能够帮助到有需要的朋友,如果觉得有帮助,还望点个赞,添加个关注,笔者也会不断地努力,写出更多更好用的文章。

相关文章:

  • 使用ffmpeg进行音频处理
  • 牛客周赛 46 F 祥子拆团
  • UE5 发射物目标追踪
  • CDN简介
  • freemarker 使用
  • Vue46-render函数
  • 收银系统源码-连锁店收银系统,支持二次开发
  • MYSQL(事务)
  • oracle发送邮件附件的步骤?怎么配置发信?
  • CUDA算子优化:矩阵乘GEMM优化(三)
  • Java 插入Mysql 报错:Column count doesn‘t match value count at row 1
  • 如何完美解决 Xshell 使用 SSH 连接 Linux 服务器报错:找不到匹配的 host key 算法
  • Linux下C程序的编写
  • 如何安全进行亚马逊、沃尔玛测评?
  • 【vue】终端 常用代码 和其他注意
  • CSS3 变换
  • css属性的继承、初识值、计算值、当前值、应用值
  • ECMAScript 6 学习之路 ( 四 ) String 字符串扩展
  • ES6核心特性
  • golang中接口赋值与方法集
  • HTML-表单
  • Java面向对象及其三大特征
  • JS创建对象模式及其对象原型链探究(一):Object模式
  • web标准化(下)
  • WePY 在小程序性能调优上做出的探究
  • 大主子表关联的性能优化方法
  • 翻译 | 老司机带你秒懂内存管理 - 第一部(共三部)
  • 反思总结然后整装待发
  • 解析带emoji和链接的聊天系统消息
  • 力扣(LeetCode)22
  • 前端工程化(Gulp、Webpack)-webpack
  • 让你的分享飞起来——极光推出社会化分享组件
  • 设计模式 开闭原则
  • HanLP分词命名实体提取详解
  • 阿里云ACE认证学习知识点梳理
  • ​sqlite3 --- SQLite 数据库 DB-API 2.0 接口模块​
  • #1014 : Trie树
  • #我与Java虚拟机的故事#连载13:有这本书就够了
  • $$$$GB2312-80区位编码表$$$$
  • (HAL)STM32F103C6T8——软件模拟I2C驱动0.96寸OLED屏幕
  • (java版)排序算法----【冒泡,选择,插入,希尔,快速排序,归并排序,基数排序】超详细~~
  • (Matalb时序预测)WOA-BP鲸鱼算法优化BP神经网络的多维时序回归预测
  • (译)2019年前端性能优化清单 — 下篇
  • (转)甲方乙方——赵民谈找工作
  • ***详解账号泄露:全球约1亿用户已泄露
  • .gitattributes 文件
  • .NET Framework 4.6.2改进了WPF和安全性
  • .NET/C# 的字符串暂存池
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • @Autowired 与@Resource的区别
  • [ C++ ] 继承
  • [2024] 十大免费电脑数据恢复软件——轻松恢复电脑上已删除文件
  • [8-27]正则表达式、扩展表达式以及相关实战
  • [C#基础知识系列]专题十七:深入理解动态类型