ReactiveCocoa创造更美好的世界
原文:ReactiveCocoa for a better world
首先说一下为什么要翻译这篇2012年的文章。ReactiveCocoa for a better world是由Josh Abernathy在2012年发表,那个时间刚好是ReactiveCocoa开源的时间,而作者Josh Abernathy正是这一开源框架的主要作者,在这篇文章中Josh Abernathy从多个角度为我们介绍了为什么要使用ReactiveCocoa以及ReactiveCocoa能为我们做什么,在这篇文章中你可能会看到一些其他介绍ReactiveCocoa文章的影子,或者更应该说是在一些其他的关于ReactiveCocoa文章中看到ReactiveCocoa for a better world的影子。因为它的影响太深了,在时隔多年重新看到它后我有了把它翻译成中文的想法。
原生App花费了大量的时间来等待和响应,我们等待用户在UI界面上做一些操作,等待网络请求返回响应,等待异步操作的完成,等待一些依赖数据发生改变,然后做出响应。
但是所有的这些事情-所有的这些等待和响应-通常都会有许多不同的处理方式,这让我们在使用统一的方法来推断他们,chain them,或者是完成它们来变得非常困难。其实,我们可以通过高级方法来做的更好。
这就为什么我们开源这个神奇的魔法: GitHub for Mac: :ReactiveCocoa
RAC是一个组合和转换序列值得框架。
认真讲,他是什么?
让我们来看一些干货,RAC给我们带来了很多非常酷的东西:
- 组合操作一些未来数据的能力
- 最小化的使用状态量和可变数据的能力
- 用一种方式定义行为和属性之间的联系
- 一个统一的,更高级的异步操作接口
- 在KVO之上封装的友好的API
这些功能看起来可能像是随机的,直到你了解到RAC可以处理这些包含了将要等待的一些新值和响应的所有事件。
最美妙的地方在于RAC是那个能够适应许多不同的,常见的场景。
说了这么多,让我们来看看RAC实际上的样子
Examples
RAC可以将遵循了KVO-compliant属性利用KVO(key-value observing)l来带来序列的值.举个例子,我们可以看到username属性的变化
1 |
[RACAble(self.username) subscribeNext:^(NSString *newName) { |
That’s cool,但是如果只有这些,那它只是一个比KOV更友好的API,RAC最酷的地方在于我们可以组合序列来一表示一些复杂的行为。
假设我们想要检查用户输入的一个特殊用户名,当用用户输入前三个值之内的时候:
1 |
[[[RACAble(self.username) |
我们发现username发生了变化,使用filter过滤发生的变化,使用take取前三个发生变化的值,如果新值是joshaber,我们就打印一个“hi mi
所以呢?
考虑一下,如果我们不使用RAC来完成这个操作,我们将不得不:
- 为username添加上KVO进行监听
- 添加一个属性来记住我们通过KVO监听到的发生变化的最后一个值
- 添加一个属性来记录我们接收到了多少个发生了变化的值
- 任何时间获取到发生变化的值得时候都要添加属性
- 对数据进行比较
还有其他的么?
我们可以组合序列:
1 |
[[RACSignal |
当任何时间password和passwordConfirmation发生变化的时候,我们组合这两个属性最后的值,然后判断这两个值是否符合我们的要求,最后返回一个BOOL。我们就可以通过这个BOOL结果来设置button是否可以点击
Bindings
We can adapt RAC to give us powerful bindings with conditions and transformations:
1 |
RAC(self, help) = [[RACObserve(self.helpLabel, text) filter:^(NSString *newHelp){ |
绑定help label的值得到属性help上,当流出的新值不是nil,就将这个新值转换成小写。(因为用户总喜欢being YELLED AT)、
Async
RAC同样非常适合做异步操作
举个例子,我们通过调用一个block来完成多次并发操作:
1 |
[[RACSignal |
或是 大专栏 翻译:ReactiveCocoa for a better world链式调用异步操作:
1 |
[[[[client |
用户登录后,首先加载缓存的消息,然后获取远程的消息,然后打印“Fetched all messages”
我们也可以方便的讲执行的操作移动到background queue
1 |
[[[[[client |
或者可以更简单的处理潜在的竞争条件,比如,我们可以使用异步操作中通过结果来更新属性,但是除非这个属性在异步操作完成前没有发生变化
1 |
[[[self |
How does it work?
RAC从根本上来看是非常简单的,它是以信号的方式来流转数据。Until you reach turtles.
Subscribers来订阅signals,Signals为它的订阅者发送 ’next’, ‘error’, ‘completed’事件,如果所有的事情都是signals来发送时间,那么关键的问题就编程了:这些事件会在什么时候被发送?
Creating Signals
信号根据合适发送时间来定义自己的行为,我们可以通过+[RACSignal createSignal:]来创建自己的signal:
1 |
RACSignal *helloWorld = [RACSignal createSignal:^(id<RACSubscriber> subscriber) { |
当这个signal获取一个新的订阅者,+[RACSignal createSignal:]会通过这个block进行回调。这个新的订阅者是通过里边的block我们可以发送这个事件。在上边的例子中,我们创建了一个signal会发送一个“hello”和“world”,然后发送完成。
嵌套signal
我们可以基于helloWorld signal来创建另一个signal:
1 |
RACSignal *joiner = [RACSignal createSignal:^(id<RACSubscriber> subscriber) { |
现在我们有了一个joiner signal,当有订阅者订阅了joiner,它会定义我们的helloworld signal.
它从helloworld和helloword完成时候增加了所有数据的接收,它将所有接收到的string连接在一起,然后发送他们和完成
在这种方式,我们可以创建signal来表达一些复杂的行为。
RAC实现了一套operations来恰好的实现他们,他们通过使用一些默认的行为来获取一些source signal 和返回一个新的signal
More info
ReactiveCocoa可以用于MAC和iOS开发,可以查看README来获取更多的信息,然后来导出MAC demo project 来获得一些实际的例子
对于.NET 开发者,所有这些听起来会很熟悉, ReactiveCocoa实际上是一个Objective-c版本的.NET Reactive Extensions)(Rx).
大多数的Rx规则应用于RAC上是非常合适的,这里是一些非常好的Rx资源:
- Reactive Extensions MSDN entry)
- Reactive Extensions for .NET Introduction
- Rx – Channel 9 videos
- Reactive Extensions wiki
- 101 Rx Samples
- Programming Reactive Extensions and LINQ (Co-authored by our own Paul Betts!)