转自  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的使用情景和用法,更多的例子可以到项目主页中查看。

参考

ReactiveCocoa与Functional Reactive Programming的更多相关文章

  1. Functional Reactive Programming

    Functional Reactive Programming (FRP) integrates time flow and compositional events into functional ...

  2. 函数式响应式编程 - Functional Reactive Programming

    我们略过概念,直接看函数式响应式编程解决了什么问题. 从下面这个例子展开: 两个密码输入框,一个提交按钮. 密码.确认密码都填写并一致,允许提交:不一致提示错误. HTML 如下: <input ...

  3. "Principles of Reactive Programming" 之 <Persistent Actor State>学习笔记

    这是<Pinciples of Reactive Programming>week6的最后一课. 为什么需要把actor的状态持久化? 如果actor没有状态,那么在任何实时,这个acto ...

  4. .Net中的反应式编程(Reactive Programming)

    系列主题:基于消息的软件架构模型演变 一.反应式编程(Reactive Programming) 1.什么是反应式编程:反应式编程(Reactive programming)简称Rx,他是一个使用LI ...

  5. Unity基于响应式编程(Reactive programming)入门

    系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoT ...

  6. "Principles of Reactive Programming" 之<Actors are Distributed> (1)

    week7中的前两节课的标题是”Actors are Distributed",讲了很多Akka Cluster的内容,同时也很难理解. Roland Kuhn并没有讲太多Akka Clus ...

  7. "reactive programming"的概念

    下面的内容大多是翻译来的. Reactive Programming? What is Reactive Programming? 为了了解Reactive——从编程范式至其背后的动机,有必要了解现在 ...

  8. [Reactive Programming] RxJS dynamic behavior

    This lesson helps you think in Reactive programming by explaining why it is a beneficial paradigm fo ...

  9. [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 ...

随机推荐

  1. 二维码识别 android app

    TextView类用于展示文本信息 ImageView展示 id:button1(用来显示“扫描二维码”) 布局:fill_parent使布满屏幕(高级版本用match_parent) wrap_co ...

  2. 公网IP映射修改后,原先的图片访问却不行了

    描述:www与img在一个公网的nginx下时,www访问img没问题   ip:*.*.*.26 www与jimg同样还在同一组nginx,改另一个公网IP映射进来的时候,却访问不了  ip:*.* ...

  3. [f]添加css3动画的方法

    添加css3的一些动画的属性 使用方法: css3(oDiv[0], 'scale', 300)('rotate', 300);css3(oDiv[0], 'animation', '"dd ...

  4. Android 5.x特性概览三

    上节,对Material Design样式做了介绍,这节我们介绍Palette. 在Android发展的长河中,UI越来越成为Google的发展重心.上文提到Android 5.x 使用palette ...

  5. C语言 稀疏矩阵 压缩 实现

    稀疏矩阵压缩存储的C语言实现 (GCC编译). /** * @brief C语言 稀疏矩阵 压缩 实现 * @author wid * @date 2013-11-04 * * @note 若代码存在 ...

  6. JavaScript获取onclick、onchange等事件的值

    今天小菜处理下拉菜单级联问题时,想获取HTML标签中某个事件的内容,也就是值,比如从<select id="city" onchange="javascript:t ...

  7. ASP.NET MVC简单编程之(二)经典路由篇

    话题:请求从路由开始 在实际的ASP.NET MVC开发中,URL访问规则----路由的定义是非常重要的.因为任何一个请求都离不开路由.理解它,我们将能理解MVC处理请求的整个过程,灵活地定义系统各种 ...

  8. java5 CountDownLatch同步工具

    好像倒计时计数器,调用CountDownLatch对象的countDown方法就将计数器减1,当到达0时,所有等待者就开始执行. java.util.concurrent.CountDownLatch ...

  9. bundle与package区别与联系

    转:http://blog.csdn.net/lmbda/article/details/17895619 bundle是Apple提供的软件安装的便捷方法. bundle为用户和开发者提供了一个简单 ...

  10. Leetcode 303 Range Sum Query - Immutable

    题意:查询一个数组在(i,j]范围内的元素的和. 思路非常简单,做个预处理,打个表就好 拓展:可以使用树状数组来完成该统计,算法复杂度为(logn),该数据结构强力的地方是实现简单,而且能完成实时更新 ...