# RAC下的响应式编程

函数响应式编程可以极大地简化项目,特别是处理嵌套回调的异步事件,复杂的列表过滤和变换,或者时间相关问题.

## 介绍

命令式编程:以命令为主,给机器提供一条又一条的命令序列让其原封不动的执行. 函数响应式编程(FRP):使用异步数据流进行编程.FRP的思想比较难理解,需要将以往的命令式编程思想转变为响应式编程思想.即面向数据流编程.

就像面向对象思想一样,把事物都看作Stream.变量.用户输入.属性.Cache.数据结构等等.这个开始可能会很难.

在命令式编程环境中,a:=b+c表示将表达式的结果赋给a,而之后改变b或c的值不会影响a.但在响应式编程中,a的值会随着b或c的更新而更新.

电子表格程序就是响应式编程的一个例子.单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化.这很类似观察者模式(Gof).函数响应式编程的重点是流,Stream能接受一个,甚至多个Stream为输入.你可以merge两个Stream,也可以从一个Stream中filter出你感兴趣的Events以生成一个新的Stream,还可以把一个Stream中的Data values map到一个新的Stream中.

## 使用RAC

RAC就是一个第三方库，他可以大大简化你的代码过程。 官方的说，ReactiveCocoa（其简称为RAC）是由GitHub开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程和响应式编程的特性。

注意 : 使用时需要引用头文件

//基本示例

```
#import <ReactiveCocoa/ReactiveCocoa.h>

//监听了textFild的UIControlEventEditingChanged事件，当事件发生时实现block
[[self.textFild rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x){
    NSLog(@"change");
}];
//文字更改监听
[[self.textFild rac_textSignal] subscribeNext:^(id x) {
    NSLog(@"%@",x);
}];

//手势监听
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^(id x) {
    NSLog(@"tap");
}];
[self.view addGestureRecognizer:tap];

//action监听
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];

[[btn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
        NSLog(@"*****  响应RAC button的点击  *****");
    }];
```

## 示例代码

一.用户登录

textfield的 内容发生改变是的响应方法

1.已通过map函数将信号转换成新的信号 2.将多个信号联合起来 3.将联合起来的信号来给按钮的属性赋值

```
//获取 textfield  的内容改变信号 并且订阅
/*当textfield中的内容发生改变的时候回触发subscribeNext（订阅）block*/
[self.firstTextfield.rac_textSignal subscribeNext:^(id x) {
  NSLog(@"%@",x);
}];

//将 textfield 输入信号的 返回值进行修改  得到新的信号！
RACSignal *firstSignal = [self.firstTextfield.rac_textSignal map:^id(NSString *firstString) {
    NSInteger length = firstString.length;
    if (length >= 5 && length <= 10) {
        return @(YES);
    }
    return @(NO);
}];

 RACSignal *secondSignal = [self.secondTextfield.rac_textSignal map:^id(NSString *secondString) {
     if (secondString.length >5 && secondString.length < 10) {
         return @(YES);
     }
     return @(NO);
 }];

// 绑定用户名.密码判断结果的三个信号量,如果都为真,则按钮可用
RAC(self.loginButton,enabled) = [RACSignal combineLatest:@[firstSignal,secondSignal] reduce:^(NSNumber *firstRes,NSNumber *secondRes){
    return @(firstRes.boolValue && secondRes.boolValue);
}];
```

二. 观察者模式KVO

RAC中得KVO大部分都是宏定义，所以代码异常简洁，简单来说就是RACObserve(TARGET, KEYPATH)这种形式，TARGET是监听目标，KEYPATH是要观察的属性值，这里举一个很简单的例子，如果UIScrollView滚动则输出success。

1.KVO来监听property的变化,只要被观察属性改变,block就会被执行. 2.该property必须支持KVO 3.使用必须在注册之后

```
UIScrollView *scrolView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 200, 400)];
scrolView.contentSize = CGSizeMake(200, 800);
scrolView.backgroundColor = [UIColor greenColor];
[self.view addSubview:scrolView];
[RACObserve(scrolView, contentOffset) subscribeNext:^(id x) {
    NSLog(@"success");
}]
```

三. taget - action

```
//button
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
    btn.frame = CGRectMake(20, 400, 300, 40);
    btn.backgroundColor = [UIColor orangeColor];
    [self.view  addSubview:btn];
    [[btn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
        NSLog(@"*****  响应RAC button的点击  *****");
    }];

//手势
self.view.userInteractionEnabled = YES;
    UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
    [[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
        NSLog(@"*****  响应单击手势  *****");
    }];
    [self.view addGestureRecognizer:tap];
```

四. 延时

```
[[RACScheduler mainThreadScheduler]afterDelay:2 schedule:^{
        NSLog(@"*****  first 延时rac写法  *****");
    }];

[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) {
        NSLog(@"***** second 延时rac写法  *****");
    }];
```

五. 通知

特别注意:RAC中的通知不需要remove observer，因为在rac\_add方法中他已经写了remove。

```
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"postData" object:nil] subscribeNext:^(NSNotification *notification) {
   NSLog(@"%@", notification.name);
   NSLog(@"%@", notification.object);
}];
```

六. 将多个不同类型的数据组合成一个元组

```
 // 把参数中的数据包装成元组
    RACTuple *tuple = RACTuplePack(@"xmg",@20,@"m",@(999),@[@"a"],@{@"key":@"value"});

    RACTupleUnpack(NSString *name,NSNumber *age,NSString *sex,NSNumber *price,NSArray *arr,NSDictionary *dic) = tuple;
    NSLog(@"name:%@  age:%@  sex:%@  price:%@ arr:%@  dic:%@",name,age,sex,price,arr,dic);
```

七. 使用 #define RAC(TARGET, ...)宏

```
//以下代码 可以将_label的text属性和后面的信号绑定信号内容发生改变的时候自动更新

RAC(_label,text) = self.textField.rac_textSignal;
```
