Pull 和 Push
命令式编程就是 Pull ,而响应式编程是 Push 。
在命令式下,我们通过调用一个方法的形式获取一个数据,这个数据可能是一个 Model 、一个状态、一个 UITextField 的输入的文本 text 。这就是一个 Pull 模型,在需要的时候拉取对应的数据。
guard let text = textField.text else { return nil }
// 根据 text 进行各种处理这就好比阅读一些优秀的博客,在想阅读的时候,打开博客网址,确认下有没有新的内容,如果有,就阅读学习一下。
而响应式的 Push 类似于订阅了该博客的 RSS ,当有更新的时候,推送给你,然后你再来决定读与不读。这就不需要我们每次都去检查一下博客状态,只需要等待更新的推送。
Rx 系列通过可观察序列 Observable 和观察者 Observer 两个类实现 Push 模型。Observer 订阅 Observable ,Observable 发送值给它的订阅者们,也就是通知所有的订阅者 Observer - 我想把这个值发给你,然后你看着处理吧。
Observer 通常都是以 closure 形式存在的,来看一个简单的例子:
// TODO: 考虑把 Subject 换回 Observable
为了更好的描述这一订阅关系,示例代码选择了 PublishSubject 代替 Observable ,我们将在 Subject 章节解释什么是 PublishSubject ,与 Observable 的关系。这里你可以把它当作一个 Observable 。
let intSequence = PublishSubject<Int>()
intSequence
.subscribeNext { value in
print("当前值为:\(value) 。")
}
// --------- 分割线 ----------
intSequence.onNext(1)
intSequence.onNext(2)
intSequence.onNext(3)
这段代码描述了 Observer 订阅了一个 intSequence ,intSequence 可能会发出一些 Int 。在建立了这样的一个订阅关系后,intSequence 推送了 1 、 2 、 3 三个值。输出结果就是:
当前值为:1 。
当前值为:2 。
当前值为:3 。
回顾一下记录 Button 点击次数的代码,不用 Rx 的部分代码如下:
dynamic private func buttonTap(sender: UIButton) {
tapCount += 1
let result = "当前点击次数:\(tapCount) 。"
print(result)
}在 buttonTap 这个 Selector 中,每次都对 tapCount 加 1 ,然后打印加 1 后的 tapCount 。这里就存在一个 Pull ,每次都要主动获取 tapCount 的值,同时进行 tapCount = tapCount + 1 。
而使用 Rx 的代码:
button
.rx_tap
.map { return 1 }
.scan(0) { acc, x return acc + x }
.map { value in return "当前点击次数:\(value) 。" }
.subscribeNext { value in
print(value)
}
则是在收到 tap 点击的推送,将推送的值进行变换,并传递下去,最终在 subscribeNext 中根据收到的结果打印当前结果。
下一节我们将专注于异步的处理,并做一个简单的 Github repo 列表的 demo 来学习如何响应式的思考问题。