ReactiveCocoa与Functional Reactive Programming
转自 http://blog.leezhong.com/ios/2013/06/19/frp-reactivecocoa.html
Functional Reactive Programming(以下简称FRP)是一种响应变化的编程范式。先来看一小段代码
a = 2
b = 2
c = a + b // c is 4
b = 3
// now what is the value of c?
如果使用FRP,c的值将会随着b的值改变而改变,所以叫做「响应式编程」。比较直观的例子就是Excel,当改变某一个单元格的内容时,该单元格相关的计算结果也会随之改变。
FRP提供了一种信号机制来实现这样的效果,通过信号来记录值的变化。信号可以被叠加、分割或合并。通过对信号的组合,就不需要去监听某个值或事件。

这在重交互的应用里是非常有用的。以注册为例:

提交按钮的状态要跟输入框的状态绑定,比如必选的输入框没有填完时,提交按钮是灰色的,也就是不可点;如果提交按钮不可点,那么文字变成灰色,不然变成蓝色;如果正在提交,那么输入框的文字颜色变成灰色,且不可点,不然变成默认色且可点;如果注册成功就在状态栏显示成功信息,不然显示错误信息,等等。
可以看到光是注册页就有这么多的联动,在javascript中可以采用事件监听来处理,iOS中更多的是delegate模式,本质上都是事件的分发和响应。这种做法的缺点是不够直观,尤其在逻辑比较复杂的情况下。这也是为什么尽管nodejs很高效,但由于javascript的callback style和异步模式不符合正常的编程习惯,让很多人望而却步。
使用FRP主要有两个好处:直观和灵活。直观的代码容易编写、阅读和维护,灵活的特性便于应对变态的需求。
ReactiveCocoa
ReactiveCocoa是github去年开源的一个项目,是在iOS平台上对FRP的实现。FRP的核心是信号,信号在ReactiveCocoa(以下简称RAC)中是通过RACSignal来表示的,信号是数据流,可以被绑定和传递。
可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径一样,这样就能保证玻璃球是依次排列,不会出现并排的情况(数据都是线性处理的,不会出现并发情况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。可以在水龙头上加一个过滤嘴(filter),不符合的不让通过,也可以加一个改动装置,把球改变成符合自己的需求(map)。也可以把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),这样只要其中的一个水龙头有玻璃球出来,这个新合并的水龙头就会得到这个球。
下面通过一个简单的demo来演示这个模型。
假如对象的某个属性想绑定某个消息,可以使用RAC这个宏,相当于给玻璃球套了一个水龙头。
RAC(self.submitButton.enabled) = [RACSignal combineLatest:@[self.usernameField.rac_textSignal, self.passwordField.rac_textSignal] reduce:^id(NSString *userName, NSString *password) {
return @(userName.length >= 6 && password.length >= 6);
}];
这样,如果用户名和密码框的长度都超过6,提交按钮就enable了。反之,如果没符合要求,就会处于非开启状态。
可以看到usernameField有了一个新的属性rac_textSignal,这是RAC在UITextFieldcategory中添加的,直接用即可。

RAC的大统一
RAC统一了对KVO、UI Event、Network request、Async work的处理,因为它们本质上都是值的变化(Values over time)。
KVO
RAC可以用来监测属性的改变,这点跟KVO很像,不过使用了block,而不是-observeValueForKeyPath:ofObject:change:context:
[RACAble(self.username) subscribeNext:^(NSString *newName) {
NSLog(@"%@", newName);
}];
使用起来是不是比KVO舒服多了。比KVO更加强大的是信号可以被链起来(chain)
// 只有当名字以'j'开头,才会被记录
[[RACAble(self.username)
filter:^(NSString *newName) {
return [newName hasPrefix:@"j"];
}]
subscribeNext:^(NSString *newName) {
NSLog(@"%@", newName);
}];
UI Event
RAC还为系统UI提供了很多category,来方便消息的创建和传递,比如按钮被点击或文本框有改动等等,上面的例子中self.firstNameField.rac_textSignal,在对应的文本框有改动时,会自动向数据流中添加新的数据,绑定该消息的其他消息就会收到新的数据,如果有subscriber的话,会自动触发。
Network Request && Async work
这些可以通过自定义信号,也就是RACSubject(继承自RACSignal,可以理解为自由度更高的signal)来搞定。比如一个异步网络操作,可以返回一个subject,然后将这个subject绑定到一个subscriber或另一个信号。
- (void)doTest
{
RACSubject *subject = [self doRequest];
[subject subscribeNext:^(NSString *value){
NSLog(@"value:%@", value);
}];
}
- (RACSubject *)doRequest
{
RACSubject *subject = [RACSubject subject];
// 模拟2秒后得到请求内容
// 只触发1次
// 尽管subscribeNext什么也没做,但如果没有的话map是不会执行的
// subscribeNext就是定义了一个接收体
[[[[RACSignal interval:2] take:1] map:^id(id _){
// the value is from url request
NSString *value = @"content fetched from web";
[subject sendNext:value];
return nil;
}] subscribeNext:^(id _){}];
return subject;
}
小结
简单画了下关系图,罗列了些要点

上面只是大概说了一下RAC的使用情景和用法,更多的例子可以到项目主页中查看。
参考
- http://www.teehanlax.com/blog/getting-started-with-reactivecocoa/
- https://speakerdeck.com/andrewsardone/reactivecocoa-at-mobidevday-2013
ReactiveCocoa与Functional Reactive Programming的更多相关文章
- Functional Reactive Programming
Functional Reactive Programming (FRP) integrates time flow and compositional events into functional ...
- 函数式响应式编程 - Functional Reactive Programming
我们略过概念,直接看函数式响应式编程解决了什么问题. 从下面这个例子展开: 两个密码输入框,一个提交按钮. 密码.确认密码都填写并一致,允许提交:不一致提示错误. HTML 如下: <input ...
- "Principles of Reactive Programming" 之 <Persistent Actor State>学习笔记
这是<Pinciples of Reactive Programming>week6的最后一课. 为什么需要把actor的状态持久化? 如果actor没有状态,那么在任何实时,这个acto ...
- .Net中的反应式编程(Reactive Programming)
系列主题:基于消息的软件架构模型演变 一.反应式编程(Reactive Programming) 1.什么是反应式编程:反应式编程(Reactive programming)简称Rx,他是一个使用LI ...
- Unity基于响应式编程(Reactive programming)入门
系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoT ...
- "Principles of Reactive Programming" 之<Actors are Distributed> (1)
week7中的前两节课的标题是”Actors are Distributed",讲了很多Akka Cluster的内容,同时也很难理解. Roland Kuhn并没有讲太多Akka Clus ...
- "reactive programming"的概念
下面的内容大多是翻译来的. Reactive Programming? What is Reactive Programming? 为了了解Reactive——从编程范式至其背后的动机,有必要了解现在 ...
- [Reactive Programming] RxJS dynamic behavior
This lesson helps you think in Reactive programming by explaining why it is a beneficial paradigm fo ...
- [Reactive Programming] Using an event stream of double clicks -- buffer()
See a practical example of reactive programming in JavaScript and the DOM. Learn how to detect doubl ...
随机推荐
- 《HTML5秘籍》学习总结--2016年7月24日
前段时间因为工作中看到同事使用了一个type为date的<input>元素,直接就形成了一个日期选择的表单控件,当时觉得很神奇,以为是什么插件,就问了同事是怎么做出来的,同事告诉我这是HT ...
- <雨季>
忘不了节后漫长的雨季,灰暗阴冷的天气,潮湿的心情,总让我怀念家乡刺眼的阳光,即使在寒冷的冬季,唯有那窗外的紫荆花独自开放.飘落满地,给灰色的世界增添点色彩,合着同学们上早课的笑声,暂时排遣那一份思乡情 ...
- Xcode7.2 导入XMPP框架错误解决
1.修改Build Settings 在 Header Search Paths 中添加: "/usr/include/libxml2" 在Other Linker Flags 中 ...
- replicate-rewrite-db
replicate-rewrite-db: Tells the slave to translate the default database (that is, the one selected b ...
- selenium通过WebDriverWait实现ajax测试,实现等页面元素加载完成
WebDriverWait(driver, 10)10秒内每隔500毫秒扫描1次页面变化,当出现指定的元素后结束. http://fox1984.iteye.com/blog/1225265new W ...
- iOS 直播(一)
由于业务需求,需要从腾讯直播sdk要迅速转移到自主开发(先让我默哀三分钟).不多说,直接开始唠嗑! 这个项目超级简单,简单到只能一个推流,一个拉流的功能.需求多的再另谈. 前期准备:推流用优酷开源的L ...
- IOS, xib和storyboard的混用
1. 从xib的viewcontroll中启动storyboard 或者 从一个storyboard切换到另一个storyboard: [objc]– (IBAction)openStoryboard ...
- C++混合编程之idlcpp教程Python篇(4)
上一篇在这 C++混合编程之idlcpp教程Python篇(3) 第一篇在这 C++混合编程之idlcpp教程(一) 与前面的工程相似,工程PythonTutorial2中,同样加入了三个文件 Pyt ...
- 一步一步搭建客服系统 (6) chrome桌面共享
本文介绍了如何在chrome下用webrtc来实现桌面共.因为必要要用https来访问才行,因此也顺带介绍了如何使用SSL证书. 1 chrome扩展程序 先下载扩展程序示例: https://git ...
- 用puthivestreaming把hdfs里的数据流到hive表
全景图: 1. 创建hive表 CREATE TABLE IF NOT EXISTS newsinfo.test( name STRING ) CLUSTERED BY (name)INTO 3 ...