RAC系列2之 基础篇

注: RAC这一些列文章主要参考了小码哥袁峥雷纯峰这两位大佬的博客

RAC简介

  1. ReactiveCocoa(简称为RAC),是由Github开源的一个应用于iOS和OS开发的新框架,Cocoa是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。
  2. ReactiveCocoa作用
    1. ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合的思想。

ReactiveCocoa编程思想

  1. ReactiveCocoa结合了几种编程风格:
    1. 函数式编程(Functional Programming)
    2. 响应式编程(Reactive Programming)
  2. ReactiveCocoa被描述为函数响应式编程(FRP)框架。
  3. 使用RAC解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。

导入ReactiveCocoa框架

  1. 之前GitHub上面的该框架是用ReactiveCocoa导入,但是现在该框架是swift版本的了,要想使用OC版本的就是ReactiveObjC了
  2. 导入:
    1. vim podfile->键盘输入i,然后编辑:

       source 'https://github.com/CocoaPods/Specs.git'
       platform :ios, '8.0'
       target 'RAC的学习'do
       use_frameworks!
       pod'ReactiveObjC'
       end
      
    2. esc->:wq->pod install

RAC常见的类

  1. RAC中:万物皆信号,任何事件通过信号传递
  2. 信号源:信号源代表的是随着时间而改变的值流,订阅者可以通过订阅信号源来获取这些值;
  3. 你可以把它想象成水龙头中的水,当你打开水龙头时,水源源不断地流出来;你也可以把它想象成电,当你插上插头时,电静静地充到你的手机上;这里的水、电就是我们所需要的值,而打开水龙头、插上插头就是订阅它们的过程。

RACSiganl

  1. 信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。
  2. 信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。
  3. 默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。
  4. 如何订阅信号:调用信号RACSignal的subscribeNext就能订阅。
  5. 简单使用

      //1.创建信号
     RACSignal *signal  = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
         //didSubscribe:当前block
         // block调用时刻:每当有订阅者订阅信号,就会调用block。
         // didSubscribe作用: 发送数据
         // 3.发送信号
         [subscriber sendNext:@1];
         // 如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。
         [subscriber sendCompleted];
            
         return [RACDisposable disposableWithBlock:^{
             //block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号
             // 执行完Block后,当前信号就不在被订阅了。
                
             NSLog(@"信号被销毁");
         }];
     }];
     //2. 订阅信号,才会激活信号(相当于响应式编程中的监听)
     [signal subscribeNext:^(id  _Nullable x) {
         //nextBlock:当前block
         // block调用时刻:每当有信号发出数据,就会调用block.
         //nextBlock作用: 处理数据展示到UI上面
         //x:信号发送内容
         NSLog(@"接收到数据:%@",x);
     } error:^(NSError * _Nullable error) {
          NSLog(@"订阅信号发生错误");
     } completed:^{
         NSLog(@"订阅完成");
     }];
    
  6. RACSignal创建方法的(create…)底层实现:
    1. 创建信号RACSignal的子类RACDynamicSignal对象,首先把didSubscribe(这个block)保存到信号RACDynamicSignal对象中(就是设置属性的意思),还不会触发。
    2. 当信号被订阅,也就是调用signal的subscribeNext:nextBlock内部做以下事情:
      1. 创建订阅者subscriber,并且把nextBlock保存到subscriber中。
      2. 调用siganldidSubscribeblock。
    3. siganldidSubscribe中调用[subscriber sendNext:@1];
    4. sendNext底层其实就是执行subscribernextBlock

RACSubscriber

  1. 表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者
  2. 这个协议声明了4个方法:
    1. 发送信号: - (void)sendNext:(nullable id)value
    2. 发送错误信息: - (void)sendError:(nullable NSError *)error;
    3. 发送完成: - (void)sendCompleted;
    4. 取消发送信号: - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
  3. 通过create创建的信号,都有一个订阅者,帮助他发送数据。

RACDisposable

  1. 用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
  2. 使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。
  3. 举例使用

      #import "ViewController.h"
     #import <ReactiveObjC/ReactiveObjC.h>
     @interface ViewController ()
        
     @property (nonatomic, strong) id<RACSubscriber> subscriber;
     @end
        
     @implementation ViewController
        
     - (void)viewDidLoad {
         [super viewDidLoad];
         // 1.创建信号
         RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber ) {
                
             _subscriber = subscriber;
                
             // 3.发送信号
             [subscriber sendNext:@"123"];
                
             return [RACDisposable disposableWithBlock:^{
                 // 只要信号取消订阅就会来这
                 // 清空资源
                 NSLog(@"信号被取消订阅了");
             }];
         }];
            
         // 2.订阅信号
         RACDisposable *disposable = [signal subscribeNext:^(id x) {
                
             NSLog(@"%@",x);
                
         }];
        
         // 默认一个信号发送数据完毕就会自动取消订阅.
         // 但是只要订阅者在,就不会自动取消信号订阅(用属性强引用着订阅者,就不会自动取消订阅了)
         // 那么我们就需要(主动取消)取消订阅信号
         [disposable dispose];
         }
        
    

RACSubject与RACReplaySubject

RACSubject

  1. 信号提供者,自己可以充当信号,又能发送信号。

     @interface RACSubject<ValueType> : RACSignal<ValueType> <RACSubscriber>
            
     /// Returns a new subject.
     + (instancetype)subject;
            
     // Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
     - (void)sendNext:(nullable ValueType)value;
                
      @end
    
  2. 使用场景:
    1. 通常用来代替代理,有了它,就不必要定义代理了。
  3. 基本使用步骤:
    1. 创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block
    2. 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
    3. 发送信号 sendNext:(id)value
  4. 代码举例:

      //1.创建信号(此时代表信号)
     RACSubject *subject = [RACSubject subject];
     //2.订阅信号
     //RACSubject处理订阅:仅仅是保存订阅者
     [subject subscribeNext:^(id  _Nullable x) {
         NSLog(@"订阅者第一次接收到数据:%@",x);
     }];
     [subject subscribeNext:^(id  _Nullable x) {
         NSLog(@"订阅者第二次接收到数据:%@",x);
     }];
     //3. 发送数据(信号)(此时代表订阅者)
     //底层实现:遍历所有的订阅者,调用nextBlock
     [subject sendNext:@"1"];
    

    打印:

     2017-12-26 11:35:48.235200+0800 RAC的学习[3905:178019] 订阅者第一次接收到数据:1
     2017-12-26 11:35:48.235323+0800 RAC的学习[3905:178019] 订阅者第二次接收到数据:1
    
  5. 底层实现:(底层实现和RACSignal不一样)
    1. [RACSubject subject]:
      1. 内部会创建一个可变数组赋值给属性
    2. 调用subscribeNext订阅信号:
      1. 创建订阅者,把nextblock赋值给订阅者属性
      2. 将该订阅者保存到属性数组中
    3. 调用sendNext发送信号:
      1. 遍历数组属性,拿到所有的订阅者,一个一个调用订阅者的nextBlock
  6. RACSubject替换代理
    1. 需求:
      1. 给当前控制器添加一个按钮,modal到另一个控制器界面
      2. 另一个控制器view中有个按钮,点击按钮,通知当前控制器
    步骤一在第二个控制器.h添加一个RACSubject代替代理
    @interface TwoViewController : UIViewController
    @property (nonatomic, strong) RACSubject *delegateSignal;
    @end
        
    步骤二监听第二个控制器按钮点击
    @implementation TwoViewController
    - (IBAction)notice:(id)sender {
        // 通知第一个控制器,告诉它,按钮被点了
            
         // 通知代理
         // 判断代理信号是否有值
        if (self.delegateSignal) {
            // 有值,才需要通知
            [self.delegateSignal sendNext:nil];
        }
    }
    @end
        
    步骤三在第一个控制器中监听跳转按钮给第二个控制器的代理信号赋值并且监听.
    @implementation OneViewController 
    - (IBAction)btnClick:(id)sender {
            
        // 创建第二个控制器
        TwoViewController *twoVc = [[TwoViewController alloc] init];
            
        // 设置代理信号
        twoVc.delegateSignal = [RACSubject subject];
            
        // 订阅代理信号
        [twoVc.delegateSignal subscribeNext:^(id x) {
               
            NSLog(@"点击了通知按钮");
        }];
            
        // 跳转到第二个控制器
        [self presentViewController:twoVc animated:YES completion:nil];
            
    }
    @end
    
    

RACReplaySubject

  1. 重复提供信号类,RACSubject的子类。
  2. RACReplaySubject使用场景
    1. 如果一个信号每被订阅一次,就需要把之前的值重复发送一遍,使用重复提供信号类
    2. 可以设置capacity数量来限制缓存的value的数量,即只缓充最新的几个值。
  3. RACReplaySubjectRACSubject区别:
    1. RACReplaySubject可以先发送信号,再订阅信号,RACSubject就不可以。
  4. 基本使用步骤
    1. 创建信号 [RACReplaySubject subject],跟RACSiganl不一样,创建信号时没有block
    2. 可以先订阅信号,也可以先发送信号。
      1. 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
      2. 发送信号sendNext:(id)value
  5. 代码举例

     //1.创建信号
     RACReplaySubject *replaySubject = [RACReplaySubject subject];
     //2.发送信号
     //1.保存值 2. 遍历所有的订阅者,发送数据
     [replaySubject sendNext:@1];
     [replaySubject sendNext:@2];
     //3. 订阅信号
     [replaySubject subscribeNext:^(id  _Nullable x) {
         NSLog(@"第一个订阅者拿到的数据===%@",x);
     }];
     [replaySubject subscribeNext:^(id  _Nullable x) {
         NSLog(@"第二个订阅者拿到的数据===%@",x);
     }];
    

    打印:

     2017-12-26 11:22:46.368676+0800 RAC的学习[3739:164003] 第一个订阅者拿到的数据===1
     2017-12-26 11:22:46.368902+0800 RAC的学习[3739:164003] 第一个订阅者拿到的数据===2
     2017-12-26 11:22:46.369426+0800 RAC的学习[3739:164003] 第二个订阅者拿到的数据===1
     2017-12-26 11:22:46.369611+0800 RAC的学习[3739:164003] 第二个订阅者拿到的数据===2
    
  6. 底层实现:(底层实现和RACSubject不一样)
    1. 创建[RACReplaySubject subject]:
      1. 尽管subject方法是父类的,但是调用init会有优先调用子类RACReplaySubjectinit
      2. 初始化一个可变数组属性用于存储值
    2. 调用sendNext发送信号:
      1. 把值保存到数组属性中
      2. 调用父类RACSubject sendNext:遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock
    3. 调用subscribeNext订阅信号:
      1. 遍历保存的所有值,一个一个调用订阅者的nextBlock(一个订阅者遍历值调用)
  7. 订阅信号与发送信号顺序不同的区别
    1. 如果先订阅再发送:
      1. 跟RACSubject效果一样
    2. 如果先发送再订阅:
      1. 也就是先保存值,在订阅值。
      2. 一旦订阅就回迅速拿到之前发送的所有值
      3. 如果想当一个信号被订阅,就重复播放之前所有值,需要先发送信号,在订阅信号。

RAC中的集合类

RACTuple

  1. 元组类,类似NSArray,用来包装值.
  2. 使用方法跟NSArray基本一样

     //1. 创建
     RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"123",@"234",@"345"]];
    //    [RACTuple tupleWithObjects:@"123",@"444", nil]
     //2. 取值
     NSString *str = tuple[0];
    //    [tuple objectAtIndex:0];
     //3. 添加(这里是返回一个新值(RACTuple),并不是可变数据的特性)
    RACTuple *tuple2 = [tuple tupleByAddingObject:@"555"];
     NSLog(@"%@",str);
     //4.RAC特有用法:遍历
     [tuple.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
         NSLog(@"======%@",x);
     }];
     //5.其他用法
     //5.1 获取数量
     NSLog(@"%zd",tuple.count);
     //5.2 获取首尾
     NSLog(@"%@===%@",tuple.first,tuple.last);
     //5.3 RACTuple转换成数组类型
     NSArray *array = [tuple allObjects];
     NSLog(@"%@",array);
     NSLog(@"tuple2===%@",[tuple2 allObjects]);
    

RACSequence

  1. RAC中的集合媒介类
  2. 作用:起到中间的媒介作用,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
    1. 把集合(NSArray,NSDictionary,RACTuple等)转换成RACSequence类: 集合.rac_sequence
    2. 把RACSequence类转换RACSignal信号类:集合.rac_sequence.signal
    3. 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。:

        [集合.rac_sequence.signal subscribeNext:^(id x) {
       NSLog(@"%@",x);
       }];
      
    4. 举例使用:
      1. 遍历数组

          NSArray *array = @[@"124",@"22",@"44"];
         //1. 把数组转换成集合RACSequence array.rac_sequence
         RACSequence *sequence = array.rac_sequence;
         //2. 把集合RACSequence转换RACSignal信号类,array.rac_sequence.signal
         RACSignal *signal = sequence.signal;
         //3. 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。
         [signal subscribeNext:^(id  _Nullable x) {
             NSLog(@"%@",x);
         }];
         //以上3步整合成一步
         [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
             NSLog(@"%@",x);
         }];
                
        
      2. 遍历字典

         //遍历出来的键值对会包装成RACTuple(元组对象)
         NSDictionary *dic = @{
                               @"name" : @"xiaoli",
                               @"year" : @20,
                               @"sex"  : @""
                               };
         //遍历
         [dic.rac_sequence.signal subscribeNext:^(RACTuple * x) {
             //打印x类型为RACTwoTuple(只放2个值,RACTuple的子类),因此在这里改变id类型为RACTuple类型
             NSLog(@"%@",x);
             //解析字典
             //方法1:
             NSString *key = x[0];
             NSString *value = x[1];
             NSLog(@"key==%@=value==%@",key,value);
             //方法2:
             //RACTupleUnpack:宏,解包元组,会把元组的值,按顺序给参数里面的变量赋值."="右边放需解析的元组.
             RACTupleUnpack(NSString *key2,NSString *value2) = x;
             NSLog(@"key2==%@=value2==%@",key2,value2);
         }];
                    
                    
        
      3. 字典转模型

         // 3.1 OC写法
         NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
                    
         NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
                    
         NSMutableArray *items = [NSMutableArray array];
         _items = items;
         for (NSDictionary *dict in dictArr) {
             FlagItem *item = [FlagItem flagWithDict:dict];
             [items addObject:item];
         }
                    
         // 3.2 RAC写法
         NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
                    
         NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
                    
         NSMutableArray *flags = [NSMutableArray array];
                    
         _flags = flags;
                    
         // rac_sequence注意点:调用subscribeNext,并不会马上执行nextBlock,而是会等一会。
         [dictArr.rac_sequence.signal subscribeNext:^(id x) {
             // 运用RAC遍历字典,x:字典
                        
             FlagItem *item = [FlagItem flagWithDict:x];
                        
             [flags addObject:item];
                        
         }];
                    
         NSLog(@"%@",  NSStringFromCGRect([UIScreen mainScreen].bounds));
                    
                    
         // 3.3 RAC高级写法:
         NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
                    
         NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
         // map:映射的意思,目的:把原始值value映射成一个新值
         // array: 把集合转换成数组
         // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。
         NSArray *flags = [[dictArr.rac_sequence map:^id(id value) {
                        
             return [FlagItem flagWithDict:value];
                        
         }] array];
        

RACMulticastConnection

  1. 用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。
  2. 注意:
    1. 只针对RACSignal的创建方式,不针对RACSubject,因为RACSubject创建方式中不含block!!!
    2. RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建.
  3. RACMulticastConnection使用步骤:
    1. 创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
    2. 创建连接 RACMulticastConnection *connect = [signal publish];
    3. 订阅信号,注意:订阅的不在是之前的信号,而是连接的信号。 [connect.signal subscribeNext:nextBlock]
    4. 连接 [connect connect]
  4. 代码举例
    1. 引出:

       //1.创建请求信号
       RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
           NSLog(@"创建信号中的block:AFN发送网络请求");
           //3.网络请求完毕,返回数据,发送信号,将数据传出
           [subscriber sendNext:@1];
           return nil;
       }];
       //2. 订阅信号
       [signal subscribeNext:^(id  _Nullable x) {
           NSLog(@"订阅者1===%@",x);
       }];
       [signal subscribeNext:^(id  _Nullable x) {
           NSLog(@"订阅者2===%@",x);
       }];
      
    2. 打印结果如下:

        2018-01-02 10:57:04.804152+0800 RAC的学习[6544:226938] 创建信号中的block:AFN发送网络请求
        2018-01-02 10:57:04.804411+0800 RAC的学习[6544:226938] 订阅者1===1
        2018-01-02 10:57:04.804694+0800 RAC的学习[6544:226938] 创建信号中的block:AFN发送网络请求
        2018-01-02 10:57:04.804855+0800 RAC的学习[6544:226938] 订阅者2===1
      
    3. 从打印可以看出:
      1. 创建信号中的block被调用了2次
      2. 引起弊端:在一个信号中发送请求,每次订阅一次都会发送请求,这样就会导致多次请求。
    4. 要求:
      1. 每次订阅不要都请求一次,只请求一次,每次订阅都要拿到第一次请求的数据
        //1.创建请求信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            NSLog(@"创建信号中的block:AFN发送网络请求");
            //4.网络请求完毕,返回数据,发送信号,将数据传出
            [subscriber sendNext:@1];
            return nil;
        }];
        //2. 创建连接(把信号转换成连接类)
        //    RACMulticastConnection *connection = [signal publish];
        RACMulticastConnection *connection = [signal multicast:[RACReplaySubject subject]];
        //3. 订阅连接类信号
        // 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext:
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"订阅者1===%@",x);
        }];
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"订阅者2===%@",x);
        }];
        //5.连接,激活信号
        [connection connect];
      
    5. 打印结果如下:

        2018-01-02 11:07:38.938790+0800 RAC的学习[6701:245354] 创建信号中的block:AFN发送网络请求
        2018-01-02 11:07:38.939491+0800 RAC的学习[6701:245354] 订阅者1===1
        2018-01-02 11:07:38.939804+0800 RAC的学习[6701:245354] 订阅者2===1
      
  5. 底层原理
    1. 创建connect:(publish/multicast)
      1. publish创建其实与multicast创建一样,因为publish内部会通过multicast创建
      2. multicast内部做如下事情:
        1. 新建RACSubject对象subect
        2. 初始化RACMulticastConnection对象connect
        3. signal赋值给connect对象的属性sourceSignal; subject赋值给connect对象的属性signal
      3. connection.signal 订阅信号
        1. 本质是拿到subject(从上面的可以看出)订阅
        2. 订阅信号,此时用的是subject订阅信号,跟sourceSignal无关,因此此时的订阅不会调用源信号的block
        3. subject订阅信号原理可知,会保存所有的订阅者
      4. [connection connect]
        1. 拿到源信号sourceSignal,订阅新信号signal(subject)(此时subject就是充当订阅者了,不是信号了) [self.sourceSignal subscribe:_signal]
        2. 因为这里只调用了源信号一次,因此,源信号的block只会调用一次,但是源信号block中传出的订阅者可是subject
        3. 源信号中的订阅者subject,发送信号: didSubscribe,拿到订阅者调用sendNext,其实是调用RACSubjectsendNext
        4. 根据subject发送信号的原理,会遍历subject保存的所有订阅者发送信号

RACCommand

  1. RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。
  2. 使用场景:监听按钮点击,网络请求
  3. RACCommand使用步骤:
    1. 创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
    2. signalBlock中,创建RACSignal,并且作为signalBlock的返回值
    3. 执行命令 - (RACSignal *)execute:(id)input
  4. RACCommand使用注意:
    1. signalBlock必须要返回一个信号,不能传nil.
    2. 如果不想要传递信号,直接创建空的信号[RACSignal empty];
    3. RACCommand中信号如果数据传递完,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。
  5. RACCommand设计思想:内部signalBlock为什么要返回一个信号,这个信号有什么用。
    1. 在RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求
    2. RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了。
  6. 如何拿到RACCommand中返回信号发出的数据。
    1. RACCommand有个执行信号源executionSignals,这个是signal of signals(信号的信号),意思是信号发出的数据是信号,不是普通的类型。
    2. 订阅executionSignals就能拿到RACCommand中返回的信号,然后订阅signalBlock返回的信号,就能获取发出的值。
  7. 监听当前命令是否正在执行executing
  8. 代码举例:
    如何拿到执行命令中产生的数据: 订阅命令内部的信号

     //1. 创建命令
     RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
         //input:执行命令传入的参数
         //signalBlock调用时刻:执行命令的时候调用
         NSLog(@"%@",input);
         // 2.创建信号,用来传递数据
         //创建空信号
    //        return [RACSignal empty];
         return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
             //发送网络请求
             NSLog(@"AFN网络请求");
             //网络请求完毕,将数据发出
             [subscriber sendNext:@"网络请求的到的数据"];
             // 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。
             [subscriber sendCompleted];
             return nil;
         }];
     }];
    

    方法1:直接订阅执行命令返回的信号(先执行命令,在订阅信号)

      //3.执行命令
     RACSignal *signal = [command execute:@1];
     //4. 订阅信号
     [signal subscribeNext:^(id  _Nullable x) {
         NSLog(@"===%@",x);
     }];
    

    方法2:先订阅信号,在执行命令

     //3. 订阅RACCommand中的信号
     //3.1 基本用法:
     //executionSignals:信号源,信号中信号signal of signals信号,发送数据就是信号
     [command.executionSignals subscribeNext:^(RACSignal * x) {
         [x subscribeNext:^(id x) {
    
             NSLog(@"%@",x);
         }];
     }
     //3.2 RAC高级用法
     //switchToLatest:用于signal of signals,获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号
     //    [command.exutionSignals.switchToLatest subscribeNext:^(id x) {
     //        
     //        NSLog(@"%@",x);
     //    }];
    

    4.监听信号(前提是订阅者发送完毕后一定要调用[subscriber sendCompleted];)
    不使用skip

      [command.executing   subscribeNext:^(NSNumber * _Nullable x) {
        //x为bool型变量
         if ([x boolValue] == YES) {
             // 正在执行
             NSLog(@"正在执行");
    
         }else{
             // 执行完成
             NSLog(@"执行完成");
         }
     }];
    

    打印结果:

     2018-01-02 15:20:22.091727+0800 RAC的学习[9676:454456] 执行完成
      2018-01-02 15:20:22.092156+0800 RAC的学习[9676:454456] 1
      2018-01-02 15:20:22.093439+0800 RAC的学习[9676:454456] 正在执行
      2018-01-02 15:20:22.093679+0800 RAC的学习[9676:454456] AFN网络请求
      2018-01-02 15:20:22.093902+0800 RAC的学习[9676:454456] 网络请求的到的数据
      2018-01-02 15:20:22.094442+0800 RAC的学习[9676:454456] 执行完成
      
    

    监听命令是否执行完毕,默认会来一次,可以直接跳过,skip表示跳过第一次信号。

      [[command.executing  skip:1] subscribeNext:^(NSNumber * _Nullable x) {
         //x为bool型变量
         if ([x boolValue] == YES) {
             // 正在执行
             NSLog(@"正在执行");
                
         }else{
             // 执行完成
             NSLog(@"执行完成");
         }
     }];
    

    打印结果:

     2018-01-0    2 15:22:58.319233+0800 RAC的学习[9784:459537] 1
      2018-01-02 15:22:58.320647+0800 RAC的学习[9784:459537] 正在执行
      2018-01-02 15:22:58.320914+0800 RAC的学习[9784:459537] AFN网络请求
      2018-01-02 15:22:58.321147+0800 RAC的学习[9784:459537] 网络请求的到的数据
      2018-01-02 15:22:58.321548+0800 RAC的学习[9784:459537] 执行完成
    

    执行命令

      //5. 执行命令
       [command execute:@1];
    
  9. 创建信号中信号

      RACSubject    *signalofsignals = [RACSubject subject];
     RACSubject *signalA = [RACSubject subject];
     RACSubject *signalB = [RACSubject subject];
     //订阅信号
     [signalofsignals.switchToLatest subscribeNext:^(id  _Nullable x) {
         NSLog(@"%@",x);
     }];
     //发送信号
     //信号发送信号
     [signalofsignals sendNext:signalA];
     [signalA sendNext:@1];
     [signalB sendNext:@"B"];
    
  10. 监听按钮点击使用举例: 自动给按钮绑定了command,点击按钮,相当于执行[command execute:btn];
@interface ViewController ()
@property (weak, nonatomic) IBOutlet UIButton *loginButton;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //使用方式1:监听按钮点击
    _loginButton.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        // comandBlock
        NSLog(@"点击了按钮%@",input);
        
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            // 信号Block
            [subscriber sendNext:input];
            
            return nil;
        }];
    }];
    
    // 监听点击结果
    [_loginButton.rac_command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    //使用方式2:监听按钮点击,同时控制按钮的使能(enable)
    //initWithEnabled
    RACSubject *enableSignal = [RACSubject subject];
    _loginButton.rac_command = [[RACCommand alloc] initWithEnabled:enableSignal signalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        // comandBlock
        NSLog(@"点击了按钮%@",input);
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            // 信号Block
            [subscriber sendNext:input];
            // 请求完成的时候去调用(不调用这一句,command永远没完成,按钮只能点击一次)
            [subscriber sendCompleted];
            
            return nil;
        }];
    }];
    
    //控制按钮的使能(加上这一句话,按钮的enable= NO)
    //[enableSignal sendNext:NO];
    
    //监听点击事件是否完成,来控制按钮的enable
    [[_loginButton.rac_command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
        BOOL executing = [x boolValue];
        //控制按钮是否能够点击
        //[enableSignal sendNext:NO];按钮的Enable= NO
        [enableSignal sendNext:@(!executing)];
    }];
    
    // 监听点击结果
   [_loginButton.rac_command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
}
@end

RACScheduler

  1. RAC中的队列,用GCD封装的。

RACUnit

  1. 表⽰stream不包含有意义的值,也就是看到这个,可以直接理解为nil.

RACEvent

  1. 把数据包装成信号事件(signal event)。它主要通过RACSignal的-materialize来使用,然并卵。
Table of Contents