美文网首页移动端开发
ReactiveCocoa 入门知识——归总篇

ReactiveCocoa 入门知识——归总篇

作者: 陈雨尘 | 来源:发表于2017-06-06 18:13 被阅读64次

    ReactiveCocoa (RAC) 是一个Cocoa框架,受Functional Reactive Programming启发。它提供Api合成变换(composing and transforming)随着时间改变的数据流
    介绍

    ReactiveCocoa来源于functional reactive programming(Input and Output)。区别于使用不断变化修改的变量,RAC提供了“事件流”,通过 SignalSignalProducer 类型来表示, 它们随着时间发送值。事件流统一了Cocoa用于事件和异步处理的常用模式,包括:
    委托方法
    回调blocks
    通知
    控件的actions和响应事件链
    Futures and promises
    Key-value observing (KVO)

    因为这些不同的机制能够用一种相同的方式处理,可以很容易的声明成链(chain)并且把它们联合在一起,用更少的代码和状态连接它们。
    基本使用


    1.创建信号

    方法1:

       RACSubscriber:表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方 v     法才 能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。
     RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
    RACSignal *siganl = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // block调用时刻:每当有订阅者订阅信号,就会调用block。
        // 2.发送信号
        [subscriber sendNext:@1];
        // 如果不在发送数据,发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号。
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            // block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
            // 执行完Block后,当前信号就不在被订阅了。
            NSLog(@"信号被销毁");
        }];
    }];
    // 3.订阅信号,才会激活信号.
    [siganl subscribeNext:^(id x) {
        // block调用时刻:每当有信号发出数据,就会调用block.
        NSLog(@"接收到数据:%@",x);
    }];
       2017-06-06 16:34:27.387 ReactiveCocoaDome[17827:6819312] 接收到数据:1
       2017-06-06 16:34:27.387 ReactiveCocoaDome[17827:6819312] 信号被销毁
    

    方法2:

     // 1.创建信号
      RACSubject *subject = [RACSubject subject];
     // 2.订阅信号
      [subject subscribeNext:^(id x) {
        // block调用时刻:当信号发出新值,就会调用.
        NSLog(@"第一个订阅者%@",x);
     }];
     [subject subscribeNext:^(id x) {
        // block调用时刻:当信号发出新值,就会调用.
        NSLog(@"第二个订阅者%@",x);
    }];
    // 3.发送信号
     [subject sendNext:@"1"];
    2017-06-06 16:34:27.388 ReactiveCocoaDome[17827:6819312] 第一个订阅者1
    2017-06-06 16:34:27.388 ReactiveCocoaDome[17827:6819312] 第二个订阅者1
    

    方法3:

      // RACReplaySubject使用步骤:
      // 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。
      // 2.可以先订阅信号,也可以先发送信号。
      // 2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
      // 2.2 发送信号 sendNext:(id)value
      // RACReplaySubject:底层实现和RACSubject不一样。
      // 1.调用sendNext发送信号,把值保存起来,然后遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。
      // 2.调用subscribeNext订阅信号,遍历保存的所有值,一个一个调用订阅者的nextBlock
      // 如果想当一个信号被订阅,就重复播放之前所有值,需要先发送信号,在订阅信号。
      // 也就是先保存值,在订阅值。
    
    // 1.创建信号
    RACReplaySubject *replaySubject = [RACReplaySubject subject];
    
    // 2.发送信号
    [replaySubject sendNext:@1];
    [replaySubject sendNext:@2];
    [replaySubject sendCompleted];
    
    // 3.订阅信号
    [replaySubject subscribeNext:^(id x) {
        
        NSLog(@"第一个订阅者接收到的数据%@",x);
    }];
    
    // 订阅信号
    [replaySubject subscribeNext:^(id x) {
        
        NSLog(@"第二个订阅者接收到的数据%@",x);
    }];
    
    
    RACDisposable * dispose = [RACDisposable disposableWithBlock:^{
        NSLog(@"replaySubject 销毁了");
      
    }];
    RACCompoundDisposable * compoundDispose = [RACCompoundDisposable compoundDisposableWithDisposables:@[dispose]];
    [replaySubject didSubscribeWithDisposable:compoundDispose];
     2017-06-06 16:34:27.550 ReactiveCocoaDome[17827:6819312] 第一个订阅者接收到的数据1
     2017-06-06 16:34:27.550 ReactiveCocoaDome[17827:6819312] 第一个订阅者接收到的数据2
     2017-06-06 16:34:27.551 ReactiveCocoaDome[17827:6819312] 第二个订阅者接收到的数据1
     2017-06-06 16:34:27.551 ReactiveCocoaDome[17827:6819312] 第二个订阅者接收到的数据2
     2017-06-06 16:34:27.551 ReactiveCocoaDome[17827:6819312] replaySubject 销毁了
    

    当然 RACReplaySubject *replaySubject = [RACReplaySubject replaySubjectWithCapacity:1];
    通过这种方式创建的 则指定了默认的储存值是1个
    打印的 则为

     2017-06-06 16:51:00.570 ReactiveCocoaDome[18117:6914095] 第一个订阅者接收到的数据2
     2017-06-06 16:51:00.570 ReactiveCocoaDome[18117:6914095] 第二个订阅者接收到的数据2
    
    2.针对不同对象的基本使用方法
    • UIButton
      添加事件
      [[_pushbut rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
      @strongify(self);
      SendViewController *sendV = [[UIStoryboard storyboardWithName:@"Main" bundle:nil]instantiateViewControllerWithIdentifier:@"SendViewController"];
      sendV.delegateSingal = [RACSubject subject];
      [sendV.delegateSingal subscribeNext:^(id x) {
      NSLog(@"点击了通知按钮 %@",x);
      }];
      [self.navigationController pushViewController:sendV animated:YES];
      }];
      触发信号

       _pushbut.rac_command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {
        return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
           [subscriber sendNext:@"发送数据"];
           
           // 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。
           [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{     
           }];
          }];
        }];
      
    • NSArray
      NSArray *numbers = @[@1,@2,@3,@4];
      // 这里其实是三步
      // 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
      // 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
      // 第三步: 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。
      [numbers.rac_sequence.signal subscribeNext:^(id x) {
      NSLog(@"数组元素 %@",x);
      }];

    • NSDictionary
      NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
      [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {

        // 解包元组,会把元组的值,按顺序给参数里面的变量赋值
        RACTupleUnpack(NSString *key,NSString *value) = x;
          // 相当于以下写法
        //        NSString *key = x[0];
        //        NSString *value = x[1];
        NSLog(@"元组对象 x == %@", x);
        NSLog(@"字典转化为 %@ %@",key,value);
       }];
      

    *字典转模型 RAC高级写法 *

      // map:映射的意思,目的:把原始值value映射成一个新值
      // array: 把集合转换成数组
      // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。
      NSArray *MMs = [[dictArr.rac_sequence map:^id(id value) {
         return [MM MMwithDic:value];
         }] array];
    
    • NSNotificationCenter

      [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil]     
       subscribeNext:^(id x) {
        NSLog(@"键盘弹出");
       }];
      
    • UITextField
      [textF.rac_textSignal subscribeNext:^(id x) {
      NSLog(@"文字改变了%@",x);
      }];

    • 定时器

      //创建定时器信号,定时8个小时
        RACSignal *signal = [RACSignal interval:60*60*8
                                    onScheduler:[RACScheduler mainThreadScheduler]];
        //定时执行代码
        [signal subscribeNext:^(id x) {
            NSLog(@"吃药");
        }];
      
        RACSignal *singal = [[[[[RACSignal interval:1 onScheduler [RACSchedulerscheduler]]take:10]startWith:@(1)]map:^id(id value) {
        NSLog(@"%@",value);
        return @"发送出去的信号";
        }]takeUntil:self.rac_willDeallocSignal];
       [singal subscribeNext:^(id x) {
        NSLog(@"x == %@",x);
       }];
      
    • 代理
      @protocol ProgrammerDelegate
      - (void)makeAnApp;
      @end

      //为self添加一个信号,表示代理ProgrammerDelegate的makeAnApp方法信号
       RACSignal *programmerSignal = [self rac_signalForSelector:@selector(makeAnApp)
                                                 fromProtocol:@protocol(ProgrammerDelegate)];
       //设置代理方法makeAnApp的实现
       [programmerSignal subscribeNext:^(RACTuple* x) {
          //这里可以理解为makeAnApp的方法要的执行代码
          NSLog(@"花了一个月,app写好了");
      }];
      //调用代理方法
      [self makeAnApp];
      
    3.其他常用
    • 观察值的变化 (监控属性等 MVVM 中比较常用)

      @weakify(self);
       [RACObserve(self, value)subscribeNext:^(id x) {
        @strongify(self);
        NSLog(@"value 发生了变化%@",self.value);
        }];
      
    • 改变信号中的值
      RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
      [subscriber sendNext:@"跳舞"];
      [subscriber sendCompleted];
      return [RACScopedDisposable disposableWithBlock:^{

        }];
        }];
      //对信号进行改进
       RAC(self,value) = [signalA map:^id(id value) {
        if ([value isEqualToString:@"跳舞"]) {
            return @"唱歌";
        }
        return @"";
      }];
      
    • 广播 通知

      NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
      //注册广播通知
      RACSignal *signal = [center rac_addObserverForName:@"代码之道频道" object:nil];
       //设置接收到通知的回调处理
      [signal subscribeNext:^(NSNotification* x) {
        NSLog(@"技巧:%@", x.userInfo[@"技巧"]);
        }];
       //发送广播通知
       [center postNotificationName:@"代码之道频道"
                          object:nil
                        userInfo:@{@"技巧":@"用心写"}];
      
    • 串联 并联
      //创建一个信号管A
      {
      RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
      //发送一个Next玻璃球和一个Complete玻璃球
      [subscriber sendNext:@"我恋爱啦"];
      [subscriber sendCompleted];
      return nil;
      }];
      //创建一个信号管B
      RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
      //发送一个Next玻璃球和一个Complete玻璃球
      [subscriber sendNext:@"我结婚啦"];
      [subscriber sendCompleted];
      return nil;
      }];
      //串联管A和管B
      RACSignal *concatSignal = [signalA concat:signalB];
      //串联后的接收端处理
      [concatSignal subscribeNext:^(id x) {
      NSLog(@"%@",x);
      }];
      //打印:我恋爱啦 我结婚啦

       }
       {
        //创建信号A
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@"纸厂污水"];
            return nil;
        }];
        //创建信号B
        RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@"电镀厂污水"];
            return nil;
        }];
        //并联2个信号
        RACSignal *mergeSignal = [RACSignal merge:@[signalA, signalB]];
        [mergeSignal subscribeNext:^(id x) {
            NSLog(@"处理%@",x);
        }];
       }
      
    • 组合
      {
      //定义2个自定义信号
      RACSubject *letters = [RACSubject subject];
      RACSubject *numbers = [RACSubject subject];
      //组合信号
      [[RACSignal combineLatest:@[letters, numbers]
      reduce:^(NSString *letter, NSString *number){
      //把2个信号的信号值进行字符串拼接
      return [letter stringByAppendingString:number];
      }] subscribeNext:^(NSString * x) {
      NSLog(@"%@", x);
      }];
      //自己控制发送信号值
      [letters sendNext:@"A"];
      [letters sendNext:@"B"];
      [numbers sendNext:@"1"];//打印B1
      [letters sendNext:@"C"];//打印C1
      [numbers sendNext:@"2"];//打印C2
      }

    • 映射
      //创建信号,发送"石"玻璃球
      RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
      [subscriber sendNext:@"石"];
      return nil;
      }];
      //对信号进行改造,改造"石"为"金"
      signal = [signal map:^id(NSString *value) {
      if ([value isEqualToString:@"石"]) {
      return @"金";
      }
      return value;
      }];
      打印
      [signal subscribeNext:^(id x) {
      NSLog(@"%@", x);//金
      }];

    • 过滤

      //创建信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            [subscriber sendNext:@(15)];
            [subscriber sendNext:@(17)];
            [subscriber sendNext:@(21)];
            [subscriber sendNext:@(14)];
            [subscriber sendNext:@(30)];
            return nil;
        }];
        //过滤信号,并打印
        [[signal filter:^BOOL(NSNumber* value) {
            //值大于等于18的才能通过过滤网
            return value.integerValue >= 18;
        }] subscribeNext:^(id x) {
            NSLog(@"%@", x);
        }];
        //打印:21 30
      
    • 命令
      //创建命令
      RACCommand *aCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(NSString *input) {
      //命令执行代码
      NSLog(@"%@我投降了",input);
      //返回一个RACSignal信号
      return [RACSignal createSignal:^RACDisposable *(id subscriber) {
      [subscriber sendCompleted];
      return nil;
      }];
      }];
      //执行命令
      [aCommand execute:@"今天"];
      //打印:今天我投降了

    • 延迟
      //创建一个信号
      RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
      NSLog(@"等等我,我还有10秒钟就到了");
      [subscriber sendNext:@"天河中心"];
      [subscriber sendCompleted];
      return nil;
      }];
      //延时10秒接受Next玻璃球
      [[signal delay:10] subscribeNext:^(NSString x) {
      NSLog(@"我到了%@",x);
      }];
      /

      [2016-04-21 13:20:10]等等我,我还有10秒钟就到了
      [2016-04-21 13:20:20]我到了天河中心
      */

    • 超时处理
      RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
      //创建发送信息信号
      NSLog(@"我快到了");
      RACSignal sendSignal = [RACSignal createSignal:^RACDisposable (id sendSubscriber) {
      [sendSubscriber sendNext:nil];
      [sendSubscriber sendCompleted];
      return nil;
      }];
      //发送信息要1个小时10分钟才到
      [[sendSignal delay:60
      70] subscribeNext:^(id x) {
      //这里才发送Next玻璃球到signal
      [subscriber sendNext:@"我到了"];
      [subscriber sendCompleted];
      }];
      return nil;
      }];
      //这里对signal进行超时接受处理
      [[signal timeout:60
      60
      onScheduler:[RACScheduler mainThreadScheduler] ]
      subscribeError:^(NSError *error)
      {
      //超时错误处理
      NSLog(@"等了你一个小时了,你还没来,我走了");
      }];

    • 重试
      __block int failedCount = 0;
      //创建信号
      RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
      if (failedCount < 100) {
      failedCount++;
      NSLog(@"我失败了");
      //发送错误,才会要重试
      [subscriber sendError:nil];
      } else {
      NSLog(@"经历了数百次失败后");
      [subscriber sendNext:nil];
      }
      return nil;
      }];
      //重试
      RACSignal *retrySignal = [signal retry];
      //直到发送了Next玻璃球
      [retrySignal subscribeNext:^(id x) {
      NSLog(@"终于成功了");
      }];

    • 节流
      RACSignal *signal = [RACSignal createSignal:^RACDisposable (id subscriber) {
      //即时发送一个Next玻璃球
      [subscriber sendNext:@"A"];
      //下面是GCD延时发送Next玻璃球
      dispatch_queue_t mainQueue = dispatch_get_main_queue();
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(1 * NSEC_PER_SEC)),mainQueue, ^{
      [subscriber sendNext:@"B"];
      });
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(2 * NSEC_PER_SEC)),mainQueue, ^{
      //发送多个Next,如果节流了,接收最新发送的
      [subscriber sendNext:@"C"];
      [subscriber sendNext:@"D"];
      [subscriber sendNext:@"E"];
      });
      dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3 * NSEC_PER_SEC)),mainQueue, ^{
      [subscriber sendNext:@"F"];
      });
      return nil;
      }];
      //对信号进行节流,限制短时间内一次只能接收一个Next玻璃球
      [[signal throttle:1] subscribeNext:^(id x) {
      NSLog(@"%@通过了",x);
      }];
      /

      A
      B
      E
      F
      *

    • 条件 (直到什么时候才 takeUntil)
      //创建取值信号
      RACSignal *takeSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
      //创建一个定时器信号,每隔1秒触发一次
      RACSignal *signal = [RACSignal interval:1
      onScheduler:[RACScheduler mainThreadScheduler]];
      //定时接收
      [signal subscribeNext:^(id x) {

                [subscriber sendNext:@"我不玩游戏除非你求我"];
            }];
            return nil;
        }];
        //创建条件信号
        RACSignal *conditionSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
            //设置5秒后发生Complete玻璃球
        
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW,
                                         (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                NSLog(@"我求你了");
                [subscriber sendCompleted];
            });
            return nil;
        }];
        //设置条件,takeSignal信号在conditionSignal信号接收完成前,不断地取值
        [[takeSignal takeUntil:conditionSignal] subscribeNext:^(id x) {
            NSLog(@"%@", x);
        }];
        /*
        我不玩游戏除非你求我
        我不玩游戏除非你求我  
        我不玩游戏除非你求我  
        我不玩游戏除非你求我  
        我不玩游戏除非你求我 
        我求你了  
         */
      
    4.常用宏
        // RAC 用于给某个对象的属性绑定
    RAC(self.sendBut.titleLabel,text) = self.textF.rac_textSignal;
    
    //RACObserve(self, name):监听某个对象的某个属性,返回的是信号。
    [RACObserve(self.view, center)subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    @weakify(self); @strongify(self);
    // RACTuplePack:把数据包装成RACTuple(元组类)
    // 把参数中的数据包装成元组
    RACTuple *tuple1 = RACTuplePack(@10,@20);
     //RACTupleUnpack:把RACTuple(元组类)解包成对应的数据。
      // 把参数中的数据包装成元组
      RACTuple *tuple = RACTuplePack(@"img",@20);
     // 解包元组,会把元组的值,按顺序给参数里面的变量赋值
     // name = @"xmg" age = @20
     RACTupleUnpack(NSString *name,NSNumber *age) = tuple;
    
    5.项目中使用示例

    网络请求
    - (RACSubject *)POST:(NSString *)path parameters:(id)parameters
    {
    RACReplaySubject *signal = [RACReplaySubject replaySubjectWithCapacity:1];
    NSURLSessionDataTask *task = [[PR_HTTPSessionManager defaultManager] POST:path parameters:[[self class] addPublicParameters:parameters] success:^(NSURLSessionDataTask *task, id responseObject){
    NSDictionary * responseDict = [NSJSONSerialization JSONObjectWithData:responseObject options:NSJSONReadingMutableContainers error:nil];

            NSError *error = [[self class] errorForResponseObject:responseDict];
            if (error) {
                 [signal sendError:error];
            } else {
                id data = responseDict[@"results"];
                [signal sendNext:data];
                [signal sendCompleted];
            }
        } failure:^(NSURLSessionDataTask *task, NSError *error) {
            
            [signal sendError:[[self class] localizedError:error]];
        }];
      
    @weakify(task);
    RACDisposable * dispose = [RACDisposable disposableWithBlock:^{
        @strongify(task);
        [task cancel];
    }];
    
    RACCompoundDisposable * compoundDispose = [RACCompoundDisposable compoundDisposableWithDisposables:@[dispose]];
    [signal didSubscribeWithDisposable:compoundDispose];
    
    return signal;
    }
    
    
      + (RACSignal *)updateUserInfo:(NSDictionary *)params {
    RACSignal *signal = [RACSignal empty];
    NSString *method = @"xxxxxx";
    signal =
        [[PR_HTTPSessionManager defaultManager] POST:method parameters:params];
    return signal;
    }  
    

    一段赋值代码

     @weakify(self);
     RAC(self.suggestTitleLabel,text) = RACObserve(self, viewModel.title);
     [[RACObserve(self, viewModel) ignore:nil] subscribeNext:^(DoctorSuggestTableViewCellModel  *cellViewModel) {
        @strongify(self);
        [self.textField setText:cellViewModel.contentString];
        self.suggestCollectionView.allowsMultipleSelection = cellViewModel.allowsMultipleSelection;
        
        [[self.textField.rac_textSignal takeUntil:self.rac_prepareForReuseSignal] subscribeNext:^(id x) {
            @strongify(self);
            self.viewModel.contentString = x;
        }];
         [[RACObserve(self.otherTextView,content) takeUntil:self.rac_prepareForReuseSignal]subscribeNext:^(id x)       {
            @strongify(self);
            self.viewModel.descriptionString = x;
        }];
    
    }]; 
    

    当然还有很多使用到的地方就不在累述 Dome地址

    以上都是入门级的使用,希望可以对你有所帮助!

    相关文章

      网友评论

        本文标题:ReactiveCocoa 入门知识——归总篇

        本文链接:https://www.haomeiwen.com/subject/zgzgfxtx.html