相信在过去的一段时间里,对 RxSwift 多少有过接触或耳闻,或者已经积累了不少实战经验。此文主要针对那些在门口徘徊,想进又拍踩坑的同学。

为什么要学习 RxSwift

当决定做一件事情时,至少要知道为什么。RxSwift 官网举了几个例子,比如可以统一处理 DelegateKVONotification,可以绑定 UI,方便网络请求的处理等等。但这些更多的是描述可以用 RxSwift 来做什么,跟为什么要使用 RxSwift 还是会有点不同。

我们先来分析下 GUI 编程的本质,我喜欢把它抽象为视图和数据的结合。其中视图负责两件事:展示和交互,展示什么由数据决定。

其中单向数据流可以通过之前介绍的 ReSwift 完成。看起来好像没 RxSwift 什么事情,其实不然,RxSwift 可以在 UniDirectional Data Flow 的各个阶段都发挥作用,从而让 Data 的处理和流动更加简洁和清晰。

  1. 通过对 RxCocoa 的各种回调进行统一处理,方便了「Interact」的处理。
  2. 通过对 Observable 的 transform 和 composite,方便了 Action 的生成(比如使用 throttle 来压缩 Action)。
  3. 通过对网络请求以及其他异步数据的获取进行 Observable 封装,方便了异步数据的处理。
  4. 通过 RxCocoa 的 binding,方便了数据的渲染。

所以 ReSwift 规范了数据流,RxSwift 为数据的处理提供了方便,这两个类库的结合,可以产生清晰的架构和易维护的代码。

当然,前提是对它们有足够的了解,尤其是 RxSwift,也就是我们今天的主角。

什么是 RxSwift

在 GUI 编程中,我认为比较复杂的有三个部分:

  1. 非原生 UI 效果的实现(比如产品经理们经常冒出来的各种想法)。
  2. 大量状态的维护。
  3. 异步数据的处理。

1)不在这次的讨论范畴(这里的学问也很多,比如流畅性和性能)。2) 可以通过单向数据流来解决(结合 Immutable Data)。3) 可以通过 RxSwift 来解决。那么 RxSwift 是如何处理异步数据的呢?

在说 RxSwift 之前,先来说下 Rx, ReactiveX 是一种编程模型,最初由微软开发,结合了观察者模式、迭代器模式和函数式编程的精华,来更方便地处理异步数据流。其中最重要的一个概念是 Observable

举个简单的例子,当别人在跟你说话时,你就是那个观察者,别人就是那个 Observable,它有几个特点:

  1. 可能会不断地跟你说话。(onNext:
  2. 可能会说错话。(onError:
  3. 结束会说话。(onCompleted

你在听到对方说的话后,也可以有几种反应:

  1. 根据说的话,做相应的事,比如对方让你借本书给他。(subscribe
  2. 把对方说的话,加工下再传达给其他人,比如对方说小张好像不太舒服,你传达给其他人时就变成了小张失恋了。(map:
  3. 参考其他人说的话再做处理,比如 A 说某家店很好吃,B 说某家店一般般,你需要结合两个人的意见再做定夺。(zip:

所以,从生活中也能看到 Rx 的影子。「有些事情急不得,你得等它自己熟」,异步,其实就是跟时间打交道,不同的时间,拿到的数据也会不一样。可以在线感受下

这里的核心是当数据有变化时,能够立刻知晓,并且通过组合和转换后,可以即时作出响应。有点像塔防,先在路上的各个节点埋好武器,然后等着小怪兽们过来。

RxSwift Workflow

大致分为这么几个阶段:先把 Native Object 变成 Observable,再通过 Observable 内置的各种强大的转换和组合能力变成新的 Observable,最后消费新的 Observable 的数据。

Native Object -> Observable

.rx extension

假设需要处理点击事件,正常的做法是给 Tap Gesture 添加一个 Target-Action,然后在那里实现具体的逻辑,这样的问题在于需要重新取名字,而且丢失了上下文。RxSwift (确切说是 RxCocoa) 给系统的诸多原生控件(包括像 URLSession)提供了 rx 扩展,所以点击的处理就变成了这样:

1
2
3
4
5
6
7
8
9
let tapBackground = UITapGestureRecognizer()

tapBackground.rx.event
.subscribe(onNext: { [weak self] _ in
self?.view.endEditing(true)
})
.addDisposableTo(disposeBag) view.addGestureRecognizer(tapBackground)

是不是简洁了很多。

Observable.create

通过这个方法,可以将 Native 的 object 包装成 Observable,比如对网络请求的封装:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public func response(_ request: URLRequest) -> Observable<(Data, HTTPURLResponse)> {
return Observable.create { observer in
let task = self.dataTaskWithRequest(request) { (data, response, error) in
observer.on(.next(data, httpResponse))
observer.on(.completed)
} task.resume() return Disposables.create {
task.cancel()
}
}
}

出于代码的简洁,略去了对 error 的处理,使用姿势类似

1
2
3
4
5
6
7
let disposeBag = DisposeBag()

response(aRequest)
.subscribe(onNext: { data in
print(data)
})
.addDisposableTo(disposeBag)

这里有两个注意点:

  1. Observerable 返回的是一个 Disposable,表示「可扔掉」的,扔哪里呢,就扔到刚刚创建的袋子里,这样当袋子被回收(dealloc)时,会顺便执行一下 Disposable.dispose(),之前创建 Disposable 时申请的资源就会被一并释放掉。
  2. 如果有多个 subscriber 来 subscribe response(aRequest) 那么会创建多个请求,从代码也可以看得出来,来一个 observer 就创建一个 task,然后执行。这很有可能不是我们想要的,如何让多个 subscriber 共享一个结果,这个后面会提到。
Variable()

Variable(value) 可以把 value 变成一个 Observable,不过前提是使用新的赋值方式 aVariable.value = newValue,来看个 Demo

1
2
3
4
5
6
7
8
9
10
11
12
13
let magicNumber = 42

let magicNumberVariable = Variable(magicNumber)
magicNumberVariable.asObservable().subscribe(onNext: {
print("magic number is \($0)")
}) magicNumberVariable.value = 73 // output
//
// magic number is 42
// magic number is 73

起初看到时,觉得还蛮神奇的,跟进去看了下,发现是通过 subject 来做的,大意是把 value 存到一个内部变量 _value 里,当调用 value 方法时,先更新 _value 值,然后调用内部的 _subject.on(.next(newValue)) 方法告知 subscriber。

Subject

Subject 简单来说是一个可以主动发射数据的 Observable,多了 onNext(value)onError(error), ‘onCompleted’ 方法,可谓全能型选手。

1
2
3
4
5
6
7
8
9
10
let disposeBag = DisposeBag()
let subject = PublishSubject<String>() subject.addObserver("1").addDisposableTo(disposeBag)
subject.onNext("

是时候学习 RxSwift 了的更多相关文章

  1. RxSwift 系列(六) -- Mathematical and Aggregate Operators

    前言 本篇文章将要学习RxSwift中数学和集合操作符,在RxSwift中包括了: toArray reduce concat toArray 将一个Observable序列转化为一个数组,并转换为一 ...

  2. RxSwift 系列(五) -- Filtering and Conditional Operators

    前言 本篇文章将要学习RxSwift中过滤和条件操作符,在RxSwift中包括了: filter distinctUntilChanged elementAt single take takeLast ...

  3. RxSwift 系列(四) -- Transforming Operators

    前言 本篇文章将要学习RxSwift中四种转换操作符: map flatMap flatMapLatest scan map 通过使用一个闭包函数将原来的Observable序列转换为一个新的Obse ...

  4. 自主学习之RxSwift(一) -----Driver

    对于RxSwift,我也是初学者,此系列来记录我学习RxSwift的历程! (一) 想必关于Drive大家一定在RxSwift的Demo中看到过,也一定有些不解,抱着一起学习的态度,来了解一下Driv ...

  5. RxSwift 系列(八) -- Error Handing Operators

    前言 本篇文章我们将学习RxSwift中的错误处理,包括: catchErrorJustReturn catchError retry retry(_:) catchErrorJustReturn 遇 ...

  6. RxSwift 系列(七) -- Connectable Operators

    前言 本篇文章将要学习RxSwift中连接操作符. Connectable Observable在订阅时不发射事件消息,而是仅当调用它们的connect()方法时才发射消息,这样就可以等待所有我们想要 ...

  7. RxSwift 介绍

    RxSwift 介绍 中文文档 https://beeth0ven.github.io/RxSwift-Chinese-Documentation/ https://medium.com/@DianQ ...

  8. RxSwift之路 1#Swift语法知识准备

    RxSwift之路 1#Swift语法知识准备 在开始学习 RxSwift 之前,一定要对 Swift 相关语法有所了解,否则就很难理解为什么可以这样.关于 Swift 的学习其实只要看看 Swift ...

  9. RxSwift之路 2#如何开始

    RxSwift之路 2#如何开始 第一步当然是把项目clone到本地,github地址:https://github.com/ReactiveX/RxSwift. 官方文档 学习的第一手资源当然是项目 ...

随机推荐

  1. 日记——OI历程

    学OI也一年多了(2015.12-),一直没学出个像样的东西.相比dalao们,我还是弱爆了. ljj,qyf,yyf三位三区dalao. xxy,myj两位三区学长dalao. 稍微总结一下前一段时 ...

  2. Vue CLI 3 中文文档

    翻译文档 文档翻译全貌 前言 之前写了一篇Vue CLI 3.x 版本的简单体验,当时文档还不全,具体的使用方法并不是很清楚,大概是2月7号,收到Vue CLI 3接近Beta版的提示,作者尤雨溪也讲 ...

  3. Golang - 爬虫案例实践

    目录 Golang - 爬虫案例实践 1. 爬虫步骤 2. 正则表达式 3. 并发爬取美图 Golang - 爬虫案例实践 1. 爬虫步骤 明确目标(确定在哪个网址搜索) 爬(爬下数据) 取(去掉没用 ...

  4. tp5 封装百度地图api接口

    //服务器端api extend\Map <?php /** * 百度地图业务封装 */ class Map{ /** * 根据地址来获取经纬度 * @param $address */ pub ...

  5. UOJ #219 BZOJ 4650 luogu P1117 [NOI2016]优秀的拆分 (后缀数组、ST表)

    连NOI Day1T1都不会做...看了题解都写不出来还要抄Claris的代码.. 题目链接: (luogu)https://www.luogu.org/problemnew/show/P1117 ( ...

  6. MySQL主要命令(3)

    //修该数据, 不要忘了set update table_name set col_name = value where 条件 //删除数据 , 指定条件对应的数据 delete from table ...

  7. axios 全攻略之基本介绍与使用(GET 与 POST)

    axios axios 是一个基于 Promise 的 HTTP 客户端,专门为浏览器和 node.js 服务 Vue 2.0 官方推荐使用 axios 来代替原来的 Vue request,所以这里 ...

  8. HDU 4512 最长公共上升子序列

    各种序列复习: (1)最长上升子序列. 1.这个问题用动态规划就很好解决了,设dp[i]是以第i个数字结尾的上升子序列的最长长度.那么方程可以是dp[i]=max(dp[j]+1).(j<i). ...

  9. C++一些知识难点

    什么是"引用"?申明和使用"引用"要注意哪些问题? 答:引用就是某个目标变量的"别名"(alias).相应用的操作与对变量直接操作效果全然同 ...

  10. Libgdx: android单机斗地主支持局域网wifi联网的网络模块核心代码

    这个作品是我近期写的,结合我的毕业设计的通信模块和之前的单机版斗地主.我已经上架到豌豆荚了,贴了点广告,看看能不能赚点茶钱. 但是一点也不乐观.因此我想分享给大家源代码. 仅仅要不用于商业. 以下先贴 ...