RxSwift使用技巧之过滤操作详解

前言

在前面的基础之上接下来我会介绍一些常用的函数和实用技巧。首先,本文将会介绍那些用于对 next 事件进行过滤的操作。这些过滤操作类似于 Swift 标准库中的 filter 操作。它能在我们开始真正进行业务处理前先把那些不符合条件的过滤掉,而且这种函数式编程的范式也能开阔我们的思维。

Ignore 过滤

RxSwift 中最简单直接的过滤操作就是 ignoreElements 了。该操作会屏蔽所有的 next 事件,只会将注意力放在 error 和 completed 事件上。如下图所示,在整个生命周期中可观察对象的所有 next 都被过滤。

示例代码:

let strikes = PublishSubject<String>()

let disposeBag = DisposeBag()

strikes
 .ignoreElements()
 .subscribe { _ in
 print("You're out!")
 }
 .addDisposableTo(disposeBag)

strikes.onNext("X")
strikes.onNext("X")
strikes.onNext("X")

strikes.onCompleted()

/* 打印结果
You're out!
*/

不过相比于残暴的全部过滤,有时候我们可能只是需要过滤某些特定的事件。例如,我们可以通过 elementAt 对特定索引号 next 进行过滤。下图演示了只响应第二个 next 事件的 elementAt 操作。

与之相应的代码为:

let strikes = PublishSubject<String>()

let disposeBag = DisposeBag()

strikes
 .elementAt(2)
 .subscribe(onNext: { str in
 print(str)
 })
 .addDisposableTo(disposeBag)

strikes.onNext("1")
strikes.onNext("2")
strikes.onNext("3")

strikes.onCompleted()

/* 打印结果
3
*/

上面两个操作最后针对的 next 事件最多只会有一个,但是大多数时候我们其实需要筛选出一组符合条件的 next 事件。下图演示的就是使用 filter 筛选数据小于 3 的操作。

图示对应代码如下:

let strikes = PublishSubject<Int>()

let disposeBag = DisposeBag()

strikes
 .filter{ $0 < 3 }
 .subscribe(onNext: { num in
 print("\(num)")
 })
 .addDisposableTo(disposeBag)

strikes.onNext(1)
strikes.onNext(2)
strikes.onNext(3)
strikes.onNext(4)
strikes.onNext(5)

strikes.onCompleted()

/* 打印结果
1
2
*/

Skip 过滤

除了忽略操作外,另一个常见的过滤就是跳过操作了。在所有的跳过操作中,最简单的就属 skip 了。通过设定参数,我们就能和简单实现跳过指定个数的事件。例如,下图久演示跳过前两个事件的操作。

let strikes = PublishSubject<Int>()

let disposeBag = DisposeBag()

strikes
 .skip(2)
 .subscribe(onNext: { num in
 print("\(num)")
 })
 .addDisposableTo(disposeBag)

strikes.onNext(1)
strikes.onNext(2)
strikes.onNext(3)
strikes.onNext(4)
strikes.onNext(5)

strikes.onCompleted()

/* 打印结果
3
4
5
*/

当然除了跳过指定索引号的事件之外,我们依旧通过 skipWhile 我们能够实现类似 filter 类似的操作。只不过 filter 会过滤整个生命周期内的符合条件的事件,而 skipWhile 在找到第一个不符合跳过操作的事件之后就不再工作。例如,下图 skipWhile 的条件是数据为奇数就跳过,但是当数据 2 执行之后 数据 3 虽然也是奇数但是不会在跳过。所以严格意义上来说 skipWhile 可能有点歧义,实际是它会跳过所有符合条件的事件,直到找到第一个能执行事件后就不再生效。

下面是跳过偶数的 skipWhile 代码:

let strikes = PublishSubject<Int>()

let disposeBag = DisposeBag()

strikes
 .skipWhile{ num in
  num % 2 == 0
 }
 .subscribe(onNext: { num in
 print("\(num)")
 })
 .addDisposableTo(disposeBag)

strikes.onNext(2)
strikes.onNext(2)
strikes.onNext(3)
strikes.onNext(4)
strikes.onNext(5)

strikes.onCompleted()

/* 打印结果
3
4
5
*/

到目前为止,上面的过滤操作都是基于一些静态条件。如果现在你需要根据其它可观察对象实例的行为进行过滤判断怎么办呢?所以接下来将会介绍涉及多实例的动态判断,其中最常见的就是 skipUntil 操作。该操作过程如下图,上面两行表示可观察对象的生命周期而最下面的表示观察者,直到第二行的可观察对象发送数据后第三行的观察者才能接受到第一行发送的数据。

图示对应代码:

let strikes = PublishSubject<String>()
let trigger = PublishSubject<String>()

let disposeBag = DisposeBag()

strikes
 .skipUntil(trigger)
 .subscribe(onNext: {
  print($0)
 })
 .addDisposableTo(disposeBag)

strikes.onNext("1")

trigger.onNext("X")

strikes.onNext("2")
strikes.onNext("3")

strikes.onCompleted()

/* 打印结果
2
3
*/

Take 过滤

这是一组与 Skip 相反的过滤操作。这组操作中最基础的操作为 take ,该操作的过程完全与 skip 相反。下图演示了 take(2) 操作的过程,它只会对前两个事件进行响应而忽略后面的事件。

上图对应代码:

let strikes = PublishSubject<String>()

let disposeBag = DisposeBag()

strikes
 .take(2)
 .subscribe(onNext: {
  print($0)
 })
 .addDisposableTo(disposeBag)

strikes.onNext("1")
strikes.onNext("2")
strikes.onNext("3")

strikes.onCompleted()

/* 打印结果
1
2
*/

除此之外,skipWhile 也有对应的 Take 操作 takeWhile ,两者的代码结构几乎一致只不过前者是跳过操作而后者则是响应操作。不过这里我不准备介绍 takeWhile 操作(可以自己动手试下),而是介绍 takeWhile 变种 takeWhileWithIndex。其实函数名已经表明了该操作的主要功能,在 takeWhile 的基础上会加上索引 index 参数。因为有时候我们除了需要通过 value 进行过滤判断外,索引 index 也可能是一个判断维度。下图就展示了 takeWhileWithIndex 简单使用示例,对于 value 和 index 值小于 1 的事件全部跳过。

图示对应代码:

let strikes = PublishSubject<Int>()

let disposeBag = DisposeBag()

strikes
 .takeWhileWithIndex { integer, index in
  integer > 1 && index > 1
 }
 .subscribe(onNext: {
  print( "\($0)")
 })
 .addDisposableTo(disposeBag)

strikes.onNext(1)
strikes.onNext(2)
strikes.onNext(3)

strikes.onCompleted()

/* 打印结果
3
*/

其实 Skip 组中同样存在与 takeWhileWithIndex 相对的 skipWhileWithIndex ,感兴趣可以自己检验一下。接下来我们介绍 Take 组中的最后一个操作 takeUntil 。同样地该操作是 skipUntil 的反操作,直到另一个实例对象触发后该实例对象的观察者才会停止响应。下图就是 takeUntil 操作的一个简单示例,作为观察者第三行会一直响应第一行可观察对象发送的数据,直到第二行对象触发后才停止。

对应代码:

let strikes = PublishSubject<String>()
let trigger = PublishSubject<String>()

let disposeBag = DisposeBag()

strikes
 .takeUntil(trigger)
 .subscribe(onNext: {
  print($0)
 })
 .addDisposableTo(disposeBag)

strikes.onNext("1")
strikes.onNext("2")

trigger.onNext("X")

strikes.onNext("3")

strikes.onCompleted()

/* 打印结果
1
2
*/

Distinct 过滤

最后本文将介绍 Distinct 过滤操作 distinctUntilChanged 。对于观察者来说,有时可观察对象可能在某段时间内连续发生相同的数据。假设这些数据与 UI 相关的话,那么这里就存在不必要的刷新操作了。所以我们有必要对过滤这些连续的相同数据,减少不必要的响应操作。下图就是一个简单的示例,图中我们过滤掉了相同的后续数据,只会对第一个作出响应。

对应示例代码:

let strikes = PublishSubject<String>()

let disposeBag = DisposeBag()

strikes
 .distinctUntilChanged()
 .subscribe(onNext: {
  print($0)
 })
 .addDisposableTo(disposeBag)

strikes.onNext("1")
strikes.onNext("2")
strikes.onNext("2")
strikes.onNext("3")

strikes.onCompleted()

/* 打印结果
1
2
3
*/

总结

本文在前面的基础上通过图示和代码介绍了主要的过滤操作。掌握好这些操作有利于我们最大化的发挥 RxSwift 功力。当然文中的代码都非常简单,所以我希望你在实际编程中不断磨练。

好了,以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • RxSwift学习之Observable的新建、订阅及取消订阅

    前言 我们在前一篇基础之上,本文将会介绍 RxSwift 中的 Observables 部分. 在 RxSwift 中 Observable 也被称为 Observable Sequence.Sequence.Stream.Observable 会以异步的方式不断的发射事件形成事件流,并且数据也会沿着事件流进行传播.下图是事件流的图像化表示: 其中从左到右的箭头代表时间轴,而三个圆圈则构成了可观察序列.而整个过程会按照从左到右的顺序.另外,事件可能在可观察序列生命周期内的任意时刻被触发. Obs

  • RxSwift学习教程之类型对象Subject详解

    前言 在上一篇文章我们介绍了 Observable 的基本概念和使用情形.但是大多数情形下,我们需要在应用运行时添加数据到 Observable 中并将其发送给订阅者.在这种需求场景下,我们就不得不使用 RxSwift 中另一种类型对象了 - Subject . 在应用中 Subject 实际上同时扮演了两个不同的角色:既是可观察对象同时也是观察者.这意味着 Subject 实例对象既可以接收事件也可以发送事件.例如,Subject 实例对象可以接收 next 事件信息,然后再将其发送给它自己的

  • RxSwift学习教程之基础篇

    前言 我们在 iOS 开发过程中,几乎无时无刻都要面对异步事件的处理.例如,按键点击.数据保存..音频后台播放.交互动画展示.这些事件并不具备特定时序性,甚至它们可能同时发生. 虽然 Apple 提供了通知.代理.GCD.闭包等异步机制,但是这些机制缺乏一个统一的抽象表述.另外,这些机制在处理共享的可变数据或状态时不够清晰简练.当然,这并不是说编写优雅的异步代码不现实.毕竟与其他平台相比 iOS 的异步机制还是很强大的. 幸运的是,我们能够通过 RxSwift 优雅的处理异步代码. 至于 RxS

  • RxSwift使用技巧之过滤操作详解

    前言 在前面的基础之上接下来我会介绍一些常用的函数和实用技巧.首先,本文将会介绍那些用于对 next 事件进行过滤的操作.这些过滤操作类似于 Swift 标准库中的 filter 操作.它能在我们开始真正进行业务处理前先把那些不符合条件的过滤掉,而且这种函数式编程的范式也能开阔我们的思维. Ignore 过滤 RxSwift 中最简单直接的过滤操作就是 ignoreElements 了.该操作会屏蔽所有的 next 事件,只会将注意力放在 error 和 completed 事件上.如下图所示,

  • 对vuex中getters计算过滤操作详解

    getter这个概念其实我们写的时候感觉好像和Mutations修改状态一样,实际上它们是有区别的: getters比较死板,如果你的百度钱包只有在金额为100才能提现,那么你在写提现页面,它是早已固定好的,而Mutation不一样,当你点击百度钱包提现,你哪怕是一元,它只要你点击了便可以提现,而且getters它是不需要什么点击,它就存在,只要你写了,这是什么意思,就是说假设你百度钱包为0,你存在了getter它就有100元,而你如果写许多百度经验,百度再次发红包0.5元时它就是100+0.5

  • Python必备技巧之字符数据操作详解

    目录 字符串操作 字符串 + 运算符 字符串 * 运算符 字符串 in 运算符 内置字符串函数 字符串索引 字符串切片 字符串切片中的步幅 将变量插入字符串 修改字符串 内置字符串方法 bytes对象 定义文字bytes对象 bytes使用内置bytes()函数定义对象 bytes对象操作,操作参考字符串. bytearray对象,Python 支持的另一种二进制序列类型 字符串操作 字符串 + 运算符 +运算符用于连接字符串,返回一个由连接在一起的操作数组成的字符串. >>> s =

  • PHP开发技巧之PHAR反序列化详解

    目录 引文 前置知识 PHAR PHAR文件结构 PHAR文件生成样例 实战 结语 引文 之前将PHP反序列化的基础知识讲了一遍,不知道大家学习的怎么样了,今天给大家带来PHP反序列化的进阶知识:PHAR反序列化,也是之前本人在CTF比赛中经常遇到的一种php反序列化的进阶使用吧,下面先给大家讲一讲PHAR反序列化的前置知识. 前置知识 PHAR 在软件中,PHAR(PHP归档)文件是一种打包格式,通过将许多PHP代码文件和其他资源(例如图像,样式表等)捆绑到一个归档文件中来实现应用程序和库的分

  • ASM的tree api对匿名线程的hook操作详解

    目录 背景 ASM介绍 class文件 fields methods InsnList Signature 实战部分 解决“匿名”Thread 最后 背景 看完本章,你将会学习到用ASM的tree api进行对匿名线程的hook操作,同时也能够了解到asm相关的操作和背景知识介绍!对于ASM插桩来说,可能很多人都不陌生了,但是大多数可能都停留在core api上,对于现在市面上的一些插桩库,其实很多都用tree api进行编写了,因为tree api的简单与明了的特性,也越来越成为许多开源库的选

  • MongoDB中的push操作详解(将文档插入到数组)

    目录 1. 概述 2. 数据库初始化 3. 使用 Mongo Query 进行推送操作 4. 使用Java驱动代码进行推送操作 4.1. 使用 DBObject 4.2. 使用 BSON 文档 5. 使用 addToSet操作符 5.1. 使用addToSet运算符的 Shell 查询 5.2. 使用addToSet运算符的 Java 驱动程序 6. 结论 总结 1. 概述 在本教程中,我们将介绍如何在MongoDB中将文档插入到数组中.此外,我们将看到 $push 和 $addToset 运算

  • node故障定位顶级技巧动态追踪Dynamic Trace详解

    目录 背景和行文目的 动态追踪技术 是什么 优势 原理 用法 demo 演示 实战演示 搭建 easy-monitor 环境 启动一个 egg 应用 制造问题 Dynamic trace 精准定位 实战总结 分享代码 动态追踪技术的未来 传统工具 潜力工具 未来 + 顶尖 总结 背景和行文目的 在做 node 或者其他语言的软件开发时,是否有以下经历: 测试环境一切正常,发到生产环境后,出现诡异问题且难以定位 不同机器.不同容器上,某些逻辑呈现不同的结果,如时区. host 对于时好时坏的玄学问

  • MySQL操作之JSON数据类型操作详解

    上一篇文章我们介绍了mysql数据存储过程参数实例详解,今天我们看看MySQL操作之JSON数据类型的相关内容. 概述 mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点.但mysql毕竟是关系型数据库,在处理json这种非结构化的数据时,还是比较别扭的. 创建一个JSON字段的表 首先先创建一个表,这个表包含一个json格式的字段: CREATE TABLE table_name ( id INT NOT NULL

  • django 常用orm操作详解

    Django流程: 1 创建Django项目 : django-admin startproject projectname 2 创建应用: : python manage.py startapp appname 3 在控制器(urls.py)创建 url 与 视图函数的映射关系(一一对应) 4 创建视图函数,完成逻辑代码 5 从数据库取出集合对象 5 把数据库变量嵌入到模板进行渲染(render方法) 6 将渲染后的html页面返回给客户端 URL:协议+域名+端口+路径 协议:http 域名

  • Android 文件操作详解及简单实例

     Android 文件操作详解 Android 的文件操作说白了就是Java的文件操作的处理.所以如果对Java的io文件操作比较熟悉的话,android的文件操作就是小菜一碟了.好了,话不多说,开始今天的正题吧. 先从一个小项目入门吧 首先是一个布局文件,这一点比较的简单,那就直接上代码吧. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="htt

随机推荐