ReactiveCocoa快速入门

最基本的使用(订阅一个信号),以TextField为例

每当textField的text属性发生变化时则emit 一个信号,这个信号可以进行二次处理(这些处理结果是返回一个signal,一般情况下是self),比如filter、map,最后subscriber可以在块里执行后续的操作.

  • filter:过滤返回值为真的信号
  • map: 把信号emit出的值类型做一个映射,比如把text值的signal映射成bool
[[self.usernameTextField.rac_textSignal
  filter:^BOOL(id value) {
    NSString *text = value;
    return text.length > 3;
  }]
  subscribeNext:^(id x) {
    NSLog(@"%@", x);
  }];
数据绑定(适用于View与ViewModel的绑定)

RAC macro的作用是把一个信号的输出赋给一个对象的某个属性,比如

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

这段代码就等价于:

[[validPasswordSignal
  map:^id(NSNumber *passwordValid) {
    return [passwordValid boolValue] ? [UIColor clearColor] : [UIColor yellowColor];
  }]
  subscribeNext:^(UIColor *color) {
    self.passwordTextField.backgroundColor = color;
  }];
多个信号还能进行合并(combine)
RACSignal *signUpActiveSignal =  
  [RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
                    reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid) {
                      return @([usernameValid boolValue] && [passwordValid boolValue]);
                    }];
  • combineLatest:接收一个signal数组
  • reduce:把多个信号进行处理,压缩成1个. 这个跟map类似
UIKIT

ReactiveCocoa不仅对UIKit controls增加了一些属性和方法,比如刚才使用的rac_textSignal, 它还封装了UIKit的事件(比如触摸)

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

当signInButton发生了touchUpInSide事件时,subscribeNext块被执行

创建一个信号以及使用

一些异步方法适用于信号. 比如一个用户登入的方法signInWithUsername:passwd:complete:

-(RACSignal *)signInSignal {
  return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
    [self
     signInWithUsername:self.usernameTextField.text
     password:self.passwordTextField.text
     complete:^(BOOL success) {
       [subscriber sendNext:@(success)];
       [subscriber sendCompleted];
     }];
    return nil;
  }];
}

使用RACSignal的createSignal:方法创建一个信号,描述这个信号的参数是一个块,当这个信号有订阅者时,块里的代码便会执行. 这个subscribe有一系列的方法用于激发事件, 比如sendNext: / sendError:/ sendCompleted . 返回值是一个RACDisposable, 这个用于当手动取消一个信号时应执行的清理动作, 你可以使用它的工厂方法:
+ (id)disposableWithBlock:(void ( ^ ) ( void ))block实现相应的清理工作. 这里因为没有需要清理的工作, 故返回nil

[[[self.signInButton
   rac_signalForControlEvents:UIControlEventTouchUpInside]
   flattenMap:^id(id x) {
     return [self signInSignal];
   }]
   subscribeNext:^(id x) {
     NSLog(@"Sign in result: %@", x);
   }];

上面的代码使用了刚才创建的信号. 注意这里使用了flattenMap:方法解决了信号嵌套的问题, 这个方法把外层信号所激发的事件用内层的信号所激发的事件替换, 听起来有点绕.

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

在信号的管道(pipeline)中, 你也可以做随意的额外操作,如上面的doNext:

comments powered by Disqus