rac初识

本文英文原文出自这篇文字 http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1 ,但我只是有选择性的进行了翻页

rac强调原子操作以及组装。rac基本上是建立在信号的基础上的,也就是RACSignal,所有的操作都能转成RACSignal来组装操作。

1、单个信号

rac入门最经典的一个例子就是一个登录界面,如下


只有当用户名和密码都满足的时候,高亮sign in 按钮。

传统的做法,你需要在delegate里面监听输入文字的变化,并做校验,这样至少同一个逻辑的代码是分散开的,而且还需要写很多额外代码,那么rac里面,会是怎样了。代码如下

[self.usernameTextField.rac_textSignal subscribeNext:^(id x) {
  NSLog(@"%@", x);
}];

一行代码足矣。。。其中 self . usernameTextField . rac_textSignal就是一个RACSignal,RAC对许多基础组建都封装了RACSignal,并不需要我们自己去创建。

在username除连续输入3个d,输出如下

2016-02-19 20:37:42.309 ReactiveExample[71930:6364937] d

2016-02-19 20:37:42.582 ReactiveExample[71930:6364937] dd

2016-02-19 20:37:42.952 ReactiveExample[71930:6364937] ddd

是不是很简单!


如果你想对用户名进行校验,如需要长度大于3,那么

RACSignal *filteredUsername = [usernameSourceSignal
                                   filter:^BOOL(id value) {
                                       NSString *text = value;
                                       return text.length > 3;
                                   }];
    
    [filteredUsername subscribeNext:^(id x) {
        NSLog(@"%@", x);
    }];


输出如下

2016-02-19 20:50:42.069 ReactiveExample[72046:6445227] dddd

2016-02-19 20:50:43.530 ReactiveExample[72046:6445227] ddddd

2016-02-19 20:50:44.858 ReactiveExample[72046:6445227] dddddd

当输入的字符个数大于3的时候,就会触发输出。

这里简单介绍两个概念 1)信号,也就是RACSignal对象  2)订阅,如上面的

[filteredUsername subscribeNext:^(id x) {

        NSLog(@"%@", x);

    }];

这里,表示对filteredUsername进行了订阅,当filteredUsername发出信号的时候,就会被订阅者感知到。

其中filter是一个rac操作,它的作用是将满足条件的usernameSourceSignal信号转化成了filteredUsername信号,rac有非常多这种操作,有兴趣的可以查看起官网文档。当然上面的代码,你也可以组合在一起,如下

[self.usernameTextField.rac_textSignal
     filter:^BOOL(NSString *text) {
         return text.length > 3;
     }]
subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];


上面的filter只是一个过滤操作,其实产生的新信号filteredUsername本质上就是usernameSourceSignal,只不过是满足一定条件的usernameSourceSignal。在rac中,你完全可以将一个信号转化成一个完成不同的信号。见如下代码

  [[[self.usernameTextField.rac_textSignal
       map:^id(NSString *text) {
           return @(text.length);
       }]
      filter:^BOOL(NSNumber *length) {
          return [length integerValue] > 3;
      }]
     subscribeNext:^(id x) {
         NSLog(@"%@", x);
     }];

跟上面只有filter进行同样的操作,如下

2016-02-19 21:03:14.344 ReactiveExample[72125:6500152] 4

2016-02-19 21:03:15.112 ReactiveExample[72125:6500152] 5

2016-02-19 21:03:15.806 ReactiveExample[72125:6500152] 6

注意对比会发现,这里输出的不再是输入的dddd ddddd dddddd,而是d的个数了,现在已经是一个完全不同的信号了。这是因为我们对

self.usernameTextField.rac_textSignal进行了map操作,形成新的信号,而这个信号传递的是@(text.length),事实上,这里我们可以传递任何对象


2、两个信号

上面只考虑了单个信号的情况,现在我们考虑两个信号的情况

RACSignal *validUsernameSignal =
  [self.usernameTextField.rac_textSignal
    map:^id(NSString *text) {
      return @([self isValidUsername:text]);
    }];
 
RACSignal *validPasswordSignal =
  [self.passwordTextField.rac_textSignal
    map:^id(NSString *text) {
      return @([self isValidPassword:text]);
    }];

现在我们做一个考虑,当用户名,或者密码正确的时候,输入框显示clearColor否则现在yellowcolor。比如对密码做这种校验

[[validPasswordSignal
  map:^id(NSNumber *passwordValid) {
    return [passwordValid boolValue] ? [UIColor clearColor] : [UIColor yellowColor];
  }]
  subscribeNext:^(UIColor *color) {
    self.passwordTextField.backgroundColor = color;
  }];
当将两者考虑在一起,可以如下

RAC(self.passwordTextField, backgroundColor) =
  [validPasswordSignal
    map:^id(NSNumber *passwordValid) {
      return [passwordValid boolValue] ? [UIColor clearColor] : [UIColor yellowColor];
    }];
 
RAC(self.usernameTextField, backgroundColor) =
  [validUsernameSignal
    map:^id(NSNumber *passwordValid) {
     return [passwordValid boolValue] ? [UIColor clearColor] : [UIColor yellowColor];
    }];

个是RAC框架提供的一种宏,用于将信号的输出直接赋值给绑定的对象。
接下来介绍怎么将这两个信号绑定到一起。回到最初的需求,我们需要在当用户名以及密码同时有效的情况下,高亮sign in按钮。这里需要用到combine操作。如下
RACSignal *signUpActiveSignal =
  [RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
                    reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid) {
                      return @([usernameValid boolValue] && [passwordValid boolValue]);
                    }];

combineLatest的作用是将最近的validUsernameSignal 以及 validPasswordSignal信号结合起来。reduce操作将这combineLatest起来的两个信号结合成一个信号,这个信号传递的值,可以根据这两个信号分别发出的信号结合起来,组成一个新的值。因此,sign in按钮的高亮可以根据如下方法来实现

[signUpActiveSignal subscribeNext:^(NSNumber *signupActive) {
   self.signInButton.enabled = [signupActive boolValue];
 }];

当sign in按钮高亮的时候,就可以开始处理sign in按钮的响应了,起响应代码如下

[[self.signInButton
   rac_signalForControlEvents:UIControlEventTouchUpInside]
   subscribeNext:^(id x) {
     NSLog(@"button clicked");
   }];

3、自己创建信号
前面使用的信号都是RAC框架自带的,很多时候,我们也需要创建属于自己的信号,创建信号如下
-(RACSignal *)signInSignal {
  return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [self.signInService
     signInWithUsername:self.usernameTextField.text
     password:self.passwordTextField.text
     complete:^(BOOL success) {
       [subscriber sendNext:@(success)];
       [subscriber sendCompleted];
     }];
    return nil;
  }];
}

这时,sign in 按钮的响应代码替换为
[[[self.signInButton
   rac_signalForControlEvents:UIControlEventTouchUpInside]
   map:^id(id x) {
     return [self signInSignal];
   }]
   subscribeNext:^(id x) {
     NSLog(@"Sign in result: %@", x);
   }];


当sign in按钮被点击的时候,signInButton会发生一个信号,注意,在以前,我们只传递了对象,而这里传递了一个信号,会有什么不同呢,这里不会输出一个值,而是会输出一串地址,因为,这里属于信号的信号,而不是普通的信号,为了正常输出,我们需要使用flattenmap,修改如下

[[[self.signInButton
  rac_signalForControlEvents:UIControlEventTouchUpInside]
  flattenMap:^id(id x) {
    return [self signInSignal];
  }]
  subscribeNext:^(NSNumber *signedIn) {
    BOOL success = [signedIn boolValue];
    self.signInFailureText.hidden = success;
    if (success) {
      [self performSegueWithIdentifier:@"signInSuccess" sender:self];
    }
  }];


flattenmap与map的区别在于,flattenmap会讲signal里面的值取出来,形成一个正常的signal,而map操作,如果碰到一个signal对象,它只是简单的将signal最为一个新的signal的值封装成一个信号的信号。

dd

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值