Update:2017-06-11:
更新一些最近找到的比较好的资料
rx对于数据流的处理
ReactiveCocoa操作方法
用 ReactiveCocoa 事半功倍的写代码(OC版本)

RAC最早是基于Objective-C的,所以在网上搜索ReactiveCocoa相关资料的话大概率会检索到Objective-C时期的教程。并且ReactiveCocoa的Swift版本—ReactiveSwift相对之前版本RAC,他的API名称以及类的名称发生了很大的变化(比如原来的signal都会带RAC前缀),所以尽管基本概念没有大变化,但旧教程对于目前的新版本RAC来说帮助还是不大。

这篇文章翻译自另一位博主的博客,他举的例子很浅显易懂。这篇文章中作者只介绍了RAC的响应式部分,函数式部分的介绍可以参照他另一篇文章链接原文中用到的是RAC4,现在最新版本已经是RAC5了,同样API的名称也发生了些改变,文章中如果有与最新RAC中不同的地方我会另外注明出来。

原文链接:Introduction to Reactive Cocoa 4 (part 1)

ReactiveCocoa4 简介

ReactiveCocoa4是目前最新的版本,相较于之前的版本,他的语法发生了很大的变化。目前网上关于RAC的最新内容都只着眼于RAC4之前版本的对比,并不是从头开始教你去如何使用RAC。如果同时你又是一个函数式编程的新手,那么RAC的学习曲线对你来说可能就会更抖一些。现在不要再去想什么是flatMaps吧,我写这篇文章正是为了那些想了解ReactiveCocoa4,但对当前RAC版本一头雾水的小白程序员(尽管他们中的一些人可能了解过了什么是函数式编程)。在开始之前,先从Github上clone一下RAC的分支,然后启动项中的Playground(官方的repo里面有自带),冲好一杯☕。如果你准备好了,就让我们开始吧。

Signals, Events, Values 这些到底讲的是啥?

打个比方,如果你要把一个Int值传到某个地方,在ReactiveCocoa4的场景中,这个Int值就是一个RAC的value类型,而传递这个value需要利用Signal作为通路,Signal可以在任意时间传递任意值。所以可以把Signal想象为一个连续不断地数据流。对于观察value的对象来说,他们需要持续观察这个value的Signal。

让我们上手试一下,假设我们需要传递一个Int值,为此我们需要创建一个Signal和与之对应的Observer,接下来我们使用这个Observer类型的对像来传递Int值。一开始这样说可能比较难懂。简单来说,这里的Observer对象不是指具体某个正在观察Signal的对象,而是指代所有正在观察这个Signal的对象。 当你用这个Observer去传递某个值时,实际上这个值会传递到所有正在观察该Signal的对象去。

一开始理解起来比较难,下面用具体的例子来示范一下如何使用RAC。首先我们需要用到pipe()函数来建立一个Signal和Observer的配对,然后试着传递一些值。

看一下这里发生了什么,首先我们利用pipe()函数产生了一个Signal和Observer的配对。Signal被设置为用来传递Int类型的值,然后我们设置Signal的闭包,每当一个值通过Signal传递,这个闭包就会被调用(成功地将回调部分代码与观察这个Signal的对象解耦)。最后我们再利用前面的Observer对象向Signal传递一些值。

正如你在Playground的图里看到的,每当有新的Int被传递时,闭包就会被调用。同时调用闭包的顺序与我们传递Int值的顺序是一致的 🎉。
但这边的Next指代的是什么呢?事实上,value被Next事件包装,然后向下发送Signal。其实并没有很多种事件能够产生Signal。
于此同时Signal的神奇之处在于他可以被任意数量的闭包所观察,所以sendNext函数中的参数可以被很多个闭包接收并处理。
注意:RAC5.0 中ObserveNext已经变更为ObserveValue

Event

上面提到当值类型被Next事件包装起来,然后向下发送Signal。除了Next事件,还有其他一些事件类型。
首先是Failed事件,Failed事件传递一个ErrorType类型的参数,并且会终止Signal活动。如果当我们遇到某种场景需要终止Signal继续活动,同时还要把异常事件的具体情况通知给所有观察者的时候。我们就可以利用sendFailed将错误信息发送给各个观察者。

最后一个值12并没有被送至各个观察者,原因就是前面说的,Failed事件已经被发送至这个信号,所以这个信号就不再继续活动了。

接下来是Completed事件,这个事件同样和Failed事件一样能够终止Singal活动。用这个事件可以表示Signal已经完成了所有任务并不再被需要了。当Completed事件发生后,就算再传递新的值给Signal,也不会再触发任何闭包的调用。

最后是Interrupted事件,这个事件和Completed事件很像,但这种类型的事件是自动发生的。还记得用Signal的observeXX函数可以给不同的事件添加闭包吗?每次我们调用完这些方法后我们都能够得到一个Disposable类型的对象,调用这个对象的dispose方法可以让我们移除这个事件对Signal的观察行为。当一个信号没有被任何事件所观察时,Interrupted事件就被调用了。比方说一棵树落在一个无人的森林中,会不会发出声音我们可不知道,因为那附近并没有人🌝。

这些看上去都很有趣,但是能将这些应用到日常的开发中去么?先看看现在已经知道了些什么

  • 一个可观察对象,观察者可以通过Signal观察value的变化
  • 一个用来发送这些value的对象(Observer)
  • 错误处理

回想下你经历过多少次更新一个model然后刷新那些正在使用这个model的对象来显示最新的内容。这样既麻烦又复杂。如果有一种方式不必使用KVO就能让你不用将对象关联起来也能及时的监听那些对象value的变化,这样会不会很赞。

这样很简洁,将所有代码逻辑(观察的设置,以及value变化时候的处理)都集中到了一个地方。只需要简单地提供一个可以让UILabel观察的Signal就可以了。不再需要任何关联、无休止的观察、或者任何同步处理。但需要特定的样板模式(对每一个需要观察的value来说需要一个Observer和一个Signal)。想象一下如果我们有一大堆拥有不同变量的model。猜猜看会发生什么?这是一个非常典型的ReactiveCocoa的使用场景,已经有人帮我们想到了,现在有请

Mutable Properties

MutableProperties将需要观察的值包装起来,不需要再像之前一样建立Signal和Observer,让我们实际看下使用MutableProperties重写后的代码。

恩,这超简洁对吧。通过MutableProperties封装一般的变量,每个变量拥有了一个对应的Signal,每当value发生变化时,就会通过Signal通知给观察者。但还没完,当value变化时,还是需要在ObserverNext事件执行更新。猜猜谁又为我们考虑到了这种场景?现在有请

Bindings

你可以只使用一个”<~”运算符将右边的值与左边的值绑定起来,每当右边的值得到更新时,左边的值也会更新。

这边我们建立了一个UILabel的子类BindableLabel,提供一个已经和UILabek成员text绑定的变量textBinding,然后我们只需要用”<~”将textBinding与其他变量绑定就可以了。

一旦有了BindablkLabel,代码就能变得十分简洁。保持label的text与username一致只需要一行代码就可以完成。更加声明化,简单化。实际上,如果把注意力只放在Model和View的建立,绑定,那代码就像下面那样

你可以将任何东西与一个Signal,Observer的配对包装起来,然后让其他对象观察,或者与一个MutableProperties绑定,然后将所有触发行为的逻辑都放到一个地方。这太强大了,然而不幸的是并不是所有的绑定都像String <~ String一样简单,我们有时候需要适当处理这些value、等待一些其他Signal的value、忽略某些value、更改某些value的类型使他们能被不同的对象使用。。。。

如何解决这些就写在我的下一篇文章中了,我们pt2再见👋

SEO:RAC5.0教程 ReactiveCocoa简介 ReactiveSwift简介