Swift Combine — zip和combineLatest的理解与使用

2024-06-22 15:28

本文主要是介绍Swift Combine — zip和combineLatest的理解与使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Publisher 上还有一些其他的操作,比如 zipcombineLatest,能让我们在时序上对控制多个 Publisher 的结果进行类似 andor 的合并,它们在构建复杂 Publisher 逻辑时也十分有用。

zip

Publisher 中的 zipSequencezip 相类似:它会把两个 (或多个) Publisher 事件序列中在同一 index 位置上的值进行合并,也就是说,Publisher1 中的第一个事件和 Publisher2 中的第一个事件结对合并,Publisher1 中的第二个事件和 Publisher2 中的第二个事件合并,以此类推:
在这里插入图片描述
比如上图中:
Publisher1 中第一个位置值为 1,在等待到 Publisher2 的第一个值 A 到来时,两者进行合并,在输出的 Publisher 中形成 (1, A)。
两个 Publisher 第二个位置的 2 和 B 也同理。
在 Publisher2 中 C 和 D 连续发生时,由于 Publisher1 里对应序列位置的事件还没有发生,因此并不会输出在最终的 Publisher 中,直到 Publisher1 里的 3 和 4 被发布。
最后 5 在 Publisher2 里并没有对应位置的 Output 事件,因此它被最终的 Publisher 忽略了。

下面的代码全完验证了上面的结论:

import SwiftUI
import Combineclass ZipViewModel: ObservableObject {private var cancellable = Set<AnyCancellable>()let numbersPub = PassthroughSubject<Int, Never>()let lettersPub = PassthroughSubject<String, Never>()@Published var receivedValues: [(Int, String)] = []func zipSample() {numbersPub.zip(lettersPub).sink(receiveValue: { value inprint("Received value: \(value)")self.receivedValues.append(value)}).store(in: &cancellable)}func sendNumber(_ number: Int) {print("Send number: \(number)")numbersPub.send(number)}func sendLetter(_ letter: String) {print("Send letter: \(letter)")lettersPub.send(letter)}
}struct ZipDemo: View {@StateObject private var viewModel = ZipViewModel()@State private var number: String = ""@State private var letter: String = ""var body: some View {VStack {VStack {TextField("Enter number...", text: $number)TextField("Enter letter...", text: $letter)}.textFieldStyle(.roundedBorder).padding()HStack {Button("Send number") {if let intNumber = Int(number) {viewModel.sendNumber(intNumber)}}.frame(maxWidth: .infinity)Button("Send letter") {if !letter.isEmpty {viewModel.sendLetter(letter)}}.frame(maxWidth: .infinity)}.buttonStyle(BorderedProminentButtonStyle())}.onAppear {viewModel.zipSample()}}
}

打印结果如下:

Send number: 1
Send letter: A
Received value: (1, "A")
Send number: 2
Send letter: B
Received value: (2, "B")
Send letter: C
Send letter: D
Send number: 3
Received value: (3, "C")
Send number: 4
Received value: (4, "D")
Send number: 5

在这里插入图片描述
zip 在时序语义上更接近于“当…且…”,当 Publisher1 发布值,且 Publisher2 发布值时,将两个值合并,作为新的事件发布出去。在实践中,zip 经常被用在合并多个异步事件的结果,比如同时发出了多个网络请求,希望在它们全部完成的时候把结果合并在一起。

combineLatest

zip 相反,combineLatest的语义接近于“当…或…”,当 Publisher1 发布值,或者 Publisher2 发布值时,将两个值合并,作为新的事件发布出去。
不论是哪个 Publisher,只要发生了新的事件,combineLatest 就把新发生的事件值和另一个 Publisher 中当前的最新值合并。

combineLatest 操作的图示如下:
在这里插入图片描述
上图中,除了首个元素 1 以外,其余的每个事件值,不论是从哪个 Publisher 发出的,都会被用来进行组合和输出。
比如在 Publisher2 输出 A 时,combineLatest 将使用这个 A 和 Publisher1 的当前最终值1 组合出结果1A。同样,随后 Publisher1 发布的 2 也和 Publisher2 的最终值 A 组合得到 2A。这个过程持续下去,直到两个 Publisher 都结束。

下面的代码验证了这一结论:

import SwiftUI
import Combineclass CombineLatestViewModel: ObservableObject {private var cancellable = Set<AnyCancellable>()let numbersPub = PassthroughSubject<Int, Never>()let lettersPub = PassthroughSubject<String, Never>()@Published var receivedValues: [(Int, String)] = []func zipSample() {numbersPub.combineLatest(lettersPub).sink(receiveValue: { value inprint("Received value: \(value)")self.receivedValues.append(value)}).store(in: &cancellable)}func sendNumber(_ number: Int) {print("Send number: \(number)")numbersPub.send(number)}func sendLetter(_ letter: String) {print("Send letter: \(letter)")lettersPub.send(letter)}
}struct CombineLatestDemo: View {@StateObject private var viewModel = CombineLatestViewModel()@State private var number: String = ""@State private var letter: String = ""var body: some View {VStack {VStack {TextField("Enter number...", text: $number)TextField("Enter letter...", text: $letter)}.textFieldStyle(.roundedBorder).padding()HStack {Button("Send number") {if let intNumber = Int(number) {viewModel.sendNumber(intNumber)}}.frame(maxWidth: .infinity)Button("Send letter") {if !letter.isEmpty {viewModel.sendLetter(letter)}}.frame(maxWidth: .infinity)}.buttonStyle(BorderedProminentButtonStyle())}.onAppear {viewModel.zipSample()}}
}

打印结果为:

Send number: 1
Send letter: A
Received value: (1, "A")
Send number: 2
Received value: (2, "A")
Send letter: B
Received value: (2, "B")
Send letter: C
Received value: (2, "C")
Send letter: D
Received value: (2, "D")
Send number: 3
Received value: (3, "D")
Send number: 4
Received value: (4, "D")
Send number: 5
Received value: (5, "D")

在这里插入图片描述

在实践中,combineLatest 被用来处理多个可变状态,在其中某一个状态发生变化时,获取这些全部状态的最新值。比如你的 UI 上有多个 TextField,你可能想要在其中某一个值变动时获取到所有 TextField 中的值并对它们进行检查,比如说用户注册界面。

写在最后

对于 zipcombineLatest,它们有一个共同特点,那就是结合后的新 Publisher 所发出的数据是元组类型。对这两种操作,一种常见的模式是将结果的发出多元组数据的 Publisher 沿着响应链继续传递,使用我们之前看到过的各类 Operator 来获取能实际驱动 UI 和 app 状态的 Publisher

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

这篇关于Swift Combine — zip和combineLatest的理解与使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1084671

相关文章

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解

《使用Python批量将.ncm格式的音频文件转换为.mp3格式的实战详解》本文详细介绍了如何使用Python通过ncmdump工具批量将.ncm音频转换为.mp3的步骤,包括安装、配置ffmpeg环... 目录1. 前言2. 安装 ncmdump3. 实现 .ncm 转 .mp34. 执行过程5. 执行结

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

C#使用Spire.Doc for .NET实现HTML转Word的高效方案

《C#使用Spire.Docfor.NET实现HTML转Word的高效方案》在Web开发中,HTML内容的生成与处理是高频需求,然而,当用户需要将HTML页面或动态生成的HTML字符串转换为Wor... 目录引言一、html转Word的典型场景与挑战二、用 Spire.Doc 实现 HTML 转 Word1

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

MyBatis ParameterHandler的具体使用

《MyBatisParameterHandler的具体使用》本文主要介绍了MyBatisParameterHandler的具体使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参... 目录一、概述二、源码1 关键属性2.setParameters3.TypeHandler1.TypeHa

Spring 中的切面与事务结合使用完整示例

《Spring中的切面与事务结合使用完整示例》本文给大家介绍Spring中的切面与事务结合使用完整示例,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考... 目录 一、前置知识:Spring AOP 与 事务的关系 事务本质上就是一个“切面”二、核心组件三、完