RAC 之引起你的兴趣

4,377 阅读4分钟

  1. UIControl

    • 监听 control 点击
    • 从此告别 addTargetbtnClick

      [[self.loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *btn) {
      // btn, self.loginBtn
      // 这里执行点击之后的操作
      }];
  2. UITextField

    • 监听 textField 的 text 改变
    • 告别 UITextFieldDelegate

      [self.myTF.rac_textSignal subscribeNext:^(NSString* text) {
      // text 即 self.myTF.text
      // 每当 text 有改变时,就会进入到这个 block
      }];
  3. UILabel

    • 把 label 的属性 text 绑定在 UITextField

      // self.myLab.text 随着 self.myTF.text 的改变而改变
      RAC(self.myLab, text) = self.myTF.rac_textSignal;
  4. 监听属性改变

    • 一旦 person 的 name 有变化,就进入 block 里面
    • 再不用写繁琐的 KVO

      [RACObserve(self.per, name) subscribeNext:^(NSString *name)
      {
      // name 即 self.per.name
      }];
  5. 监听通知信息

    • 一旦键盘 frame 有所变化,就进入 block 里面

    • 内部已经进行通知释放,不需要再 dealloc 中 移除

      [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillChangeFrameNotification object:nil] subscribeNext:^(NSNotification *notification) {
      NSLog(@"-----%@", notification.description);
      }];
    • 发送通知还是之前的办法

      [[NSNotificationCenter defaultCenter] postNotificationName:@"通知名称" object:nil];
  6. 数组

    • 遍历

      NSArray *array = @[@1, @2, @3, @4, @5];
      [array.rac_sequence.signal subscribeNext:^(id x) {
      // x 即 数组 array 的元素
      }];
    • 过滤 filter,并获取过滤后的数组

      NSArray *filter = [[array.rac_sequence filter:^BOOL(id value) {
      return [value integerValue] > 2;
      }] array];
    • 匹配、映射 map,变换元素并获取新数组

      NSArray *map = [[array.rac_sequence map:^id(id value) {
      NSInteger a = [value integerValue] * [value integerValue];
      return [NSString stringWithFormat:@"%ld", a];
      }] array];
  7. 字典

    • rac_keySequencerac_valueSequence 跟数组一样
    • rac_sequence 需要 RACTupleUnpack 解包

      NSDictionary *dic = @{@"name": @"lion", @"age": @18};
      [dic.rac_sequence.signal subscribeNext:^(id x) {
      RACTupleUnpack(NSString *key, NSString *value) = x;
      NSLog(@"\r\nkey: %@\r\nvalue: %@", key, value);
      }];
  8. 最经典的登录界面,登录按钮是否可点击

    • 先联合两个信号
    • 再解析信号结果
    • 最后把结果绑定到信号上

      RAC(self.loginBtn, enabled) = [RACSignal
      combineLatest:@[self.usernameTF.rac_textSignal, self.passwordTF.rac_textSignal]
      reduce:^id(NSString *username, NSString *password){
      return @(username.length > 6 && password.length > 8);
      }];
  9. 节流 throttle

    • 表示 指定时间间隔内,不再发送信号
    • 这里添加 throttle, 表示在 0.5 秒内 text 没有改变时,才会进行搜索请求
    • 比如想搜索 reactiveCocoa,如果不添加 throttle,那么每输入一个字符,都会进行搜索请求,明显不是我们想要的。

      [[[self.searchTF rac_textSignal]
      throttle:0.5]
      subscribeNext:^(id x) {
      NSLog(@"开始搜索请求==%@", x);
      }];
  10. 一通组合拳:

    1. 判断用户名是否符合规则,

    2. 用户名正确之后,进行判断密码是否符合规则

    3. 密码正确,进行请求验证,成功之后返回请求结果

    4. 返回主线程根据结果更新界面 UI

// 1. 判断用户名
- (RACSignal *)validUsernameSignal {
// 防止循环引用
@weakify(self);
// 因为返回的是一个信号,所以需要创建
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
[[[[[self.usernameTF rac_textSignal]
// skip, 忽略,表示忽略几次信号
// 这里两次分别是,首次加载的时候,和 TF 第一次响应的时候,
// 可以把后面的 distinctUntilChanged 注释,测试下效果
skip:2]
// map, 变换,把信号内容的类型变换为另一种类型
// 把字符串根据长度转换为 bool 类型的对象
map:^id(NSString *value) {
return @(value.length > 5);
// distinctUntilChanged 只有值不同时,才会发送信号
// 这里为了防止,在值不满足/已满足 要求时,还继续发送信号
}] distinctUntilChanged]
// 这里订阅的信息,就是转换后的 bool 类型的对象,非 1 即 0
subscribeNext:^(id x) {
if ([x integerValue] == 1) {
[subscriber sendNext:@"用户名正确"];
[subscriber sendCompleted];
NSLog(@"-------用户名正确");
} else {
NSLog(@"-------用户名错误");
}
}];
return nil;
}];
}
// 3. 请求验证
- (RACSignal *)loginSuccessSignalWithPassword: (NSString *)psw {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
/**
* 这里可以进行请求验证用户名密码,并返回结果
*/
NSString *str = @"用户信息 Data";
NSLog(@"----登陆成功并返回用户信息 Data");
NSDictionary *dic = @{@"data": str};
[subscriber sendNext:dic];
[subscriber sendCompleted];
return nil;
}];
}
// 2、4、密码验证和界面更新
- (void)loginResult {
// 用户名的判断
[[[[[[[self validUsernameSignal]
// then, 只有前面的信号 发送信息,并完成才会继续
// 下面进行 密码的判断
then:^RACSignal *{
return self.passwordTF.rac_textSignal;
}]
// throttle,间隔 0.5 秒,
// 防止在输入密码过程中不断发送信号,优化性能
throttle:0.5]
// filter,过滤 ,只有符合要求的才能继续
filter:^BOOL(NSString *value) {
return [value length] > 6;
}]
// flattenMap 根据源信号的内容生成新的信号,后续订阅、监听的就是新信号的内容
// 常用于在信号嵌套中,处理信号中的信号
// 这里使用 flattenMap 生成新的信号,并且在信号内发送登录结果信息,以便后续传递信息
flattenMap:^RACStream *(NSString *value) {
return [self loginSuccessSignalWithPassword:value];
}]
// deliverOn 切换线程,
// RACScheduler, 即 RAC 中的多线程
// 切换为主线程
deliverOn:[RACScheduler mainThreadScheduler]]
// 订阅信号,获取信号传递的数据 data
subscribeNext:^(id x) {
NSLog(@"获取信息 \r\n%@,\r\n更新 UI", x);
}];
}

小结:

  • ReactiveCocoa 虽然说是思想上的巨大改变,但我更倾向于把它当做一种新型语法、更简便的语法来使用,在使用过程中,自然而然地就会体会它跟你以往编程的不同。使用多了,就会发现,原来这就是函数式响应式编程。

  • 理论是实践出来的,在你不懂理论的时候,那就赶紧实践吧。