RAC(ReactiveCocoa)学习资源汇总~持续更新

来源:互联网 发布:淘宝店铺信誉度怎么看 编辑:程序博客网 时间:2024/05/11 19:28

传送门:http://blog.csdn.net/shuidonglch/article/details/56836296

朋友介绍RAC不错,在git上一搜,竟然有一万三的星星,遂下下来试下水,发现非常好用,无奈编程思想还不太习惯,里面要学的东西也很多,所以,这次特意开一个专题用来学习,记录自己学习RAC的旅程;


今天看了一篇博客,对照着上面的知识点全部手敲了一遍代码,对于新知识我一向秉承多看不如多敲的思想(主要是因为本人比较笨啦,看的记不住,O(∩_∩)O)



03~15 03~17 RAC的学习今天暂告一段落 , 接下来开始RAC的使用了,以下贴上整个学习的相关代码,等我整理好后将上传到git上


- (NSArray *)dataArrString{

    if (!_dataArrString){

        _dataArrString = [@"A B C D E F G H I"componentsSeparatedByString:@" "];

    }

    return_dataArrString;

}



- (NSArray *)dataArrNumber{

    if (!_dataArrNumber){

        _dataArrNumber = [@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "];

    }

    return_dataArrNumber;

}


- (void)viewDidLoad {

    [superviewDidLoad];

    

    [selftestAllRAC];

    return;

    

    self.signInService = [RWDummySignInServicenew];

    self.signInFailureText.hidden =YES;

    

    [[self.usernameTextField.rac_textSignalfilter:^BOOL(NSString* value) {

        return value.length >3;

    }] subscribeNext:^(id x) {

        NSLog(@"%@",x);

    }];

    

    

    RACSignal* validUsernameSignal = [self.usernameTextField.rac_textSignalmap:^id(NSString* text) {

        return@([selfisValidUsername:text]);

    }];

    

    RAC(self.usernameTextField , backgroundColor) = [validUsernameSignalmap:^id(NSNumber *usernameValid) {

        return [usernameValidboolValue] ? [UIColorclearColor]:[UIColoryellowColor];

    }];

    

    

    [[[[self.signInButtonrac_signalForControlEvents:UIControlEventTouchUpInside]doNext:^(id x) {

        self.signInButton.enabled =NO;

        self.signInFailureText.hidden =YES;

    }] flattenMap:^id(id value) {

        return [selfsignInSignal];

    }]

      subscribeNext:^(NSNumber*signedIn) {

          self.signInFailureText.enabled =YES;

          BOOL success =[signedInboolValue];

          self.signInFailureText.hidden = success;

          if(success){

              [selfperformSegueWithIdentifier:@"signInSuccess"sender:self];

          }

    }];

    

    [selfusernameTextFieldChanged];

    [selfpasswordTextFieldChanged];

    [selfupdateUIState];

}


- (void)testRegister{

    [RACObserve(self, usernameTextField.text)subscribeNext:^(NSString* text) {

        //        NSLog(@"text : %@",text);

    }];

    [[RACObserve(self.usernameTextField, text)filter:^BOOL(NSString* text) {

        returnYES;

    }] subscribeNext:^(NSString* text) {

        //        NSLog(@"text2 : %@",text);

    }];

    

    //Signals能够用来导出状态,RAC通过signals and operations让表示属性变得有可能

    //连接两个属性的值,只有当两个属性的值满足一定的条件时,按钮才可以被点击

    RAC(self , signInButton.enabled) = [RACSignalcombineLatest:@[RACObserve(self.usernameTextField, text),RACObserve(self.passwordTextField, text)]reduce:^(NSString* userName ,NSString* password){

        //        NSLog(@"username : %@  password : %@",userName , password);

        return@(![userNameisEqualToString:password]);

    }];

    

    // 按钮的点击

    self.signInButton.rac_command = [[RACCommandalloc]initWithSignalBlock:^RACSignal *(id input) {

        NSLog(@"signInButton button was pressed!");

        return [RACSignalempty];

    }];

    

    

}


- (void)testAllRAC{

    [selftestRegister];

    

    RACSequence* letters =self.dataArrString.rac_sequence;

    RACSignal* letter = letters.signal;

    // 依次输出 A B C D…

    [letter subscribeNext:^(NSString* text) {

//        NSLog(@"text %@",text);

    }];

    

    // 测试注入效果 -doNext: -doError: -doCompleted:

//    [self testNextAndCompleted];

    

    // 测试 map:映射,可以看做对玻璃球的变换、重新组装

//    [self testMap];

    

    // 测试 filter:过滤,不符合要求的玻璃球不允许通过

//    [self testFilter];

    

    // 测试 concat:把一个水管拼接到另一个水管之后

//    [self testConcat];

    

    // 测试 flatten :

//    [self testFlatten];

    

    // 测试 map and flatten

//    [self testMapAndFlatten];

    

    // 测试 combine

//    [self testCombining];

    

    // 测试 switching

//    [self testSwitching];

    

    // 测试RAC RAC可以看作某个属性的值与一些信号的联动

//    [self testRAC];

    

    // 测试 RACObserve监听属性的改变,使用blockKVO

//    [self testRACObserve];

    

    // 网络测试

//    [self testNetwork];

    

    // 实现一个时钟应用

//    [self testClockApplication];

    

    // 测试组合 combine

//    [self testCombine];

    

    // 测试 RACSubject的使用

//    [self testSubject];

    

    // 测试 RACReplaySubject的使用

//    [self testReplaySubject];

    

    // 测试 RACTuple RACSequence的使用

//    [self testSequenceAndTuple];

    

    // 测试 RACCommand :用于处理事件的类

//    [self testCommand];

    

    // 测试 RACMulticastConnection的使用

//    [self testMulticastConnection];

    

    // 测试 RAC开发中常见用法

//    [self testRACMethod];

    

    // 测试 RAC开发中常见宏

    [selftestMacroDefinition];

}


#pragma mark - 测试 RAC开发中常见宏

- (void)testMacroDefinition{

    /*

     8.1 RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定

     // 只要文本框文字改变,就会修改label的文字

     RAC(self.labelView,text) = _textField.rac_textSignal;

     

     8.2 RACObserve(self, name):监听某个对象的某个属性,返回的是信号

     [RACObserve(self.view, center) subscribeNext:^(id x) {

     NSLog(@"%@",x);

     }];

     

     8.3  @weakify(Obj)@strongify(Obj),一般两个都是配套使用,在主头文件(ReactiveCocoa.h)中并没有导入,需要自己手动导入,RACEXTScope.h才可以使用。但是每次导入都非常麻烦,只需要在主头文件自己导入就好了

     

     8.4 RACTuplePack:把数据包装成RACTuple(元组类)

     // 把参数中的数据包装成元组

     RACTuple *tuple = RACTuplePack(@10,@20);

     

     8.5 RACTupleUnpack:把RACTuple(元组类)解包成对应的数据

     // 把参数中的数据包装成元组

     RACTuple *tuple = RACTuplePack(@"xmg",@20);

     

     // 解包元组,会把元组的值,按顺序给参数里面的变量赋值

     // name = @"xmg" age = @20

     RACTupleUnpack(NSString *name,NSNumber *age) = tuple;

     */

}


#pragma mark - 测试 RACMulticastConnection的使用 :用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理

- (void)testMulticastConnection{

    // 使用注意:RACMulticastConnection通过RACSignal-publish或者-muticast:方法创建

    /*

     RACMulticastConnection使用步骤:

     1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

     2.创建连接 RACMulticastConnection *connect = [signal publish];

     3.订阅信号,注意:订阅的不在是之前的信号,而是连接的信号。 [connect.signal subscribeNext:nextBlock]

     4.连接 [connect connect]

     

     RACMulticastConnection底层原理:

     1.创建connectconnect.sourceSignal -> RACSignal(原始信号)  connect.signal -> RACSubject

     2.订阅connect.signal,会调用RACSubjectsubscribeNext,创建订阅者,而且把订阅者保存起来,不会执行block

     3.[connect connect]内部会订阅RACSignal(原始信号),并且订阅者是RACSubject

         3.1.订阅原始信号,就会调用原始信号中的didSubscribe

         3.2 didSubscribe,拿到订阅者调用sendNext,其实是调用RACSubjectsendNext

     4.RACSubjectsendNext,会遍历RACSubject所有订阅者发送信号。

         4.1 因为刚刚第二步,都是在订阅RACSubject,因此会拿到第二步所有的订阅者,调用他们的nextBlock

     

     需求:假设在一个信号中发送请求,每次订阅一次都会发送请求,这样就会导致多次请求。

     解决:使用RACMulticastConnection就能解决

     */

    

    // 1.创建请求信号

    RACSignal* signal = [RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        NSLog(@"发送请求");

        [subscriber sendNext:@1];

        returnnil;

    }];

    

    // 2.订阅信号

    [signal subscribeNext:^(id x) {

        NSLog(@"接收数据");

    }];

    

    // 2.订阅信号

    [signal subscribeNext:^(id x) {

        NSLog(@"接收数据");

    }];

    

//    return;

    // 3.运行结果,会执行两遍发送请求,也就是每次订阅都会发送一次请求

    

    // RACMulticastConnection:解决重复请求问题

    // 1.创建信号

    RACSignal* signal2 = [RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        NSLog(@"再次发送请求");

        [subscriber sendNext:@1];

        returnnil;

    }];

    

    // 2.创建连接

    RACMulticastConnection* connect = [signal2publish];

    

    // 3.订阅信号,

    // 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext:

    [connect.signalsubscribeNext:^(id x) {

        NSLog(@"订阅者一信号 %@",x);

    }];

    

    [connect.signalsubscribeNext:^(id x) {

        NSLog(@"订阅者二信号 %@",x);

    }];

    

    // 4.连接,激活信号

    [connect connect];

}


#pragma mark - 测试 RACCommand :用于处理事件的类

- (void)testCommand{

    /*

     RACCommand:RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程

     

     一、RACCommand使用步骤:

     1.创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock

     2.signalBlock中,创建RACSignal,并且作为signalBlock的返回值

     3.执行命令 - (RACSignal *)execute:(id)input

     

     二、RACCommand使用注意:

     1.signalBlock必须要返回一个信号,不能传nil.

     2.如果不想要传递信号,直接创建空的信号[RACSignal empty];

     3.RACCommand中信号如果数据传递完,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。

     4.RACCommand需要被强引用,否则接收不到RACCommand中的信号,因此RACCommand中的信号是延迟发送的

     

     三、RACCommand设计思想:内部signalBlock为什么要返回一个信号,这个信号有什么用。

     1.RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求。

     2.RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了

     

     四、如何拿到RACCommand中返回信号发出的数据。

     1.RACCommand有个执行信号源executionSignals,这个是signal of signals(信号的信号),意思是信号发出的数据是信号,不是普通的类型。

     2.订阅executionSignals就能拿到RACCommand中返回的信号,然后订阅signalBlock返回的信号,就能获取发出的值

     

     五、监听当前命令是否正在执行executing

     

     六、使用场景,监听按钮点击,网络请求

     */

    

    // 1. 创建命令

    RACCommand* command = [[RACCommandalloc]initWithSignalBlock:^RACSignal *(id input) {

        NSLog(@"执行命令");

        // 创建空信号,必须返回信号

//                return [RACSignal empty];

        

        // 2.创建信号,用来传递数据

        return [RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

            [subscriber sendNext:@"请求数据"];

            

            // 注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕

            [subscriber sendCompleted];

            returnnil;

        }];

    }];

    

    // 强引用命令,不要被销毁,否则接收不到数据

    _command = command;

    

    // 3.订阅RACCommand中的信号

    [command.executionSignalssubscribeNext:^(id x) {

        [x subscribeNext:^(id x) {

            NSLog(@"x : %@",x);

        }];

    }];

    

    // RAC高级用法

    // switchToLatest:用于signal of signals,获取signal of signals发出的最新信号,也就是可以直接拿到RACCommand中的信号

    [command.executionSignals.switchToLatestsubscribeNext:^(id x) {

        NSLog(@"switchToLatest : %@",x);

    }];

    

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

    [[command.executingskip:1]subscribeNext:^(id x) {

        if ([xboolValue] ==YES){

            // 正在执行

            NSLog(@"正在执行");

        }else{

            // 执行完成

            NSLog(@"执行完成");

        }

    }];

    

    // 5. 执行命令

    [self.commandexecute:@1];

}


#pragma mark - 测试 RACTuple RACSequence的使用

- (void)testSequenceAndTuple{

    /*

     RACTuple:元组类,类似NSArray,用来包装值

     RACSequence:RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典

     使用场景:1.字典转模型

     RACSequenceRACTuple简单使用

     */

    // 1.遍历数组

    NSArray *numbers =@[@1,@2,@3,@4];

    

    // 这里其实是三步

    // 第一步:把数组转换成集合RACSequence numbers.rac_sequence

    // 第二步:把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal

    // 第三步:订阅信号,激活信号,会自动把集合中的所有值,遍历出来。

    [numbers.rac_sequence.signalsubscribeNext:^(id x) {

        NSLog(@"x : %@",x);

    }];

    

    // 2.遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)

    NSDictionary *dict =@{@"name":@"xmg",@"age":@18};

    [dict.rac_sequence.signalsubscribeNext:^(RACTuple *x) {

        // 解包元组,会把元组的值,按顺序给参数里面的变量赋值

        RACTupleUnpack(NSString *key,NSString *value) = x;

        

        // 相当于以下写法

        //        NSString *key = x[0];

        //        NSString *value = x[1];

        NSLog(@"key : %@ value : %@",key,value);

    }];

    

#pragma mark - 3.字典转模型

#pragma mark - 3.1 OC写法

    /*

     NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];

     

     NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];

     

     NSMutableArray *items = [NSMutableArray array];

     

     for (NSDictionary *dict in dictArr) {

     FlagItem *item = [FlagItem flagWithDict:dict];

     [items addObject:item];

     }

     */

    

#pragma mark - 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];

     

     }];

     */

    

#pragma mark - 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];

     */

}


/*

 需求:

 1.给当前控制器添加一个按钮,modal到另一个控制器界面

 2.另一个控制器view中有个按钮,点击按钮,通知当前控制器

 */

#pragma mark - RACSubject替换代理

- (void)testSubjectReplaceDelegate{

    /*

     步骤一:在第二个控制器.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

     */

}


#pragma mark - RAC部分知识汇总

- (void)RACSomeKnowledge{

    /*

     1. 如何取消订阅一个signal?在一个completed或者error事件之后,订阅会自动移除)

     2. 你还可以通过RACDisposable手动移除订阅,RACSignal的订阅方法都会返回一个RACDisposable实例,它能让你通过dispose方法手动移除订阅

     3. 如果你创建了一个管道,但是没有订阅它,这个管道就不会执行,包括任何如doNext: block的附加操作

     */

    

    /*

     RACSignal使用步骤:

     1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

     2.订阅信号,才会激活信号. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

     3.发送信号 - (void)sendNext:(id)value

     

     RACSignal底层实现:

     1.创建信号,首先把didSubscribe保存到信号中,还不会触发。

     2.当信号被订阅,也就是调用signalsubscribeNext:nextBlock

         2.2 subscribeNext内部会创建订阅者subscriber,并且把nextBlock保存到subscriber中。

         2.1 subscribeNext内部会调用siganldidSubscribe

     3.siganldidSubscribe中调用[subscriber sendNext:@1];

         3.1 sendNext底层其实就是执行subscribernextBlock

     

     如果不在发送数据,最好发送信号完成(sendCompleted),内部会自动调用[RACDisposable disposable]取消订阅信号。

     [subscriber sendCompleted];

     

     RACSubscriber:表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据

     

     RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它

     使用场景:不想监听某个信号时,可以通过它主动取消订阅信号

     

     RACSubject:RACSubject:信号提供者,自己可以充当信号,又能发送信号

     使用场景:通常用来代替代理,有了它,就不必要定义代理了

       RACReplaySubject:重复提供信号类,RACSubject的子类

       RACReplaySubjectRACSubject区别:

       RACReplaySubject可以先发送信号,在订阅信号,RACSubject就不可以。

       使用场景一:如果一个信号每被订阅一次,就需要把之前的值重复发送一遍,使用重复提供信号类。

       使用场景二:可以设置capacity数量来限制缓存的value的数量,即只缓充最新的几个值

     

     [self testSubject];

     [self testReplaySubject];

     

     

     RACScheduler:RAC中的队列,用GCD封装的

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

     RACEvent: 把数据包装成信号事件(signal event)。它主要通过RACSignal-materialize来使用,然并卵

     */

    

    /*

#pragma mark - ReactiveCocoa开发中常见用法

     rac_signalForSelector:用于替代代理

     rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变

     rac_signalForControlEvents:用于监听某个事件

     rac_addObserverForName:用于监听某个通知

     rac_textSignal:只要文本框发出改变就会发出这个信号

     rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组),每一个signal都至少sendNext过一次,就会去触发第一个selector参数的方法使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据

     // 代码演示

     [self testRACMethod];

     */

}


- (void)btnClick:(UIButton* )button{

    NSLog(@"btnClick");

}


#pragma mark - ReactiveCocoa开发中常见用法

- (void)testRACMethod{

    UIView* redV = [[UIViewalloc]initWithFrame:CGRectMake(100,100,100,100)];

    _redV = redV;

//    [self.view addSubview:redV];

    // 1.代替代理

    // 需求:自定义redView,监听红色view中按钮点击

    // 之前都是需要通过代理监听,给红色View添加一个代理属性,点击按钮的时候,通知代理做事情

    // rac_signalForSelector:把调用某个对象的方法的信息转换成信号,只要调用这个方法,就会发送信号

    // 这里表示只要redV调用btnClick:,就会发出信号,订阅就好了

    [[redV rac_signalForSelector:@selector(btnClick:)]subscribeNext:^(id x) {

        NSLog(@"点击红色按钮");

    }];

    

    // 2.KVO

    // 把监听redVcenter属性改变转换成信号,只要值改变就会发送信号

    // observer:可以传入nil

    [[redV rac_valuesAndChangesForKeyPath:@"center"options:NSKeyValueObservingOptionNewobserver:nil]subscribeNext:^(id x) {

        NSLog(@"KVO %@",x);

    }];

    // 模拟center属性的改变

//    redV.center = CGPointMake(200, 100);

    

    // 3.监听事件

    // 把按钮点击事件转换为信号,点击按钮,就会发送信号

    [[self.signInButtonrac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {

        NSLog(@"signInButton按钮被点击了");

    }];

    

    // 4.代替通知

    // 把监听到的通知转换信号

    [[[NSNotificationCenterdefaultCenter]rac_addObserverForName:UIKeyboardWillShowNotificationobject:nil]subscribeNext:^(id x) {

        NSLog(@"键盘弹出");

    }];

    [[[NSNotificationCenterdefaultCenter]rac_addObserverForName:UIKeyboardWillHideNotificationobject:nil]subscribeNext:^(id x) {

        NSLog(@"键盘消失");

    }];

    

    // 5.监听文本框的文字改变

    [self.usernameTextField.rac_textSignalsubscribeNext:^(id x) {

        NSLog(@"文字改变了%@",x);

    }];

    

    // 6.处理多个请求,都返回结果的时候,统一做处理.

    RACSignal *request1 = [[RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        // 发送请求2

        [subscriber sendNext:@"发送请求1"];

        returnnil;

    }] throttle:5.0];

     

    RACSignal *request2 = [RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        // 发送请求2

        [subscriber sendNext:@"发送请求2"];

        returnnil;

    }];

    

    // 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。

    [selfrac_liftSelector:@selector(updateUIWithR1:r2:)withSignalsFromArray:@[request1,request2]];

}


// 更新UI

- (void)updateUIWithR1:(id)data r2:(id)data1

{

    NSLog(@"更新UI%@  %@",data,data1);

}


- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{

    [self.viewendEditing:YES];

}


#pragma mark - 测试 RACSubject的使用

- (void)testSubject{

    /*

     RACSubject使用步骤

     1.创建信号 [RACSubject subject],跟RACSignal不一样,创建信号时没有block

     2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

     3.发送信号 sendNext:(id)value

     */

    // 1.创建信号

    RACSubject *subject = [RACSubjectsubject];

    

    [subject sendNext:@"1"];

    [subject sendNext:@"2"];

    [subject sendNext:@"3"];

    

    // 2.订阅信号

    [subject subscribeNext:^(id x) {

        // block调用时刻:当信号发出新值,就会调用.

        NSLog(@"第一个订阅者%@",x);

    }];

    [subject subscribeNext:^(id x) {

        // block调用时刻:当信号发出新值,就会调用.

        NSLog(@"第二个订阅者%@",x);

    }];

    

    // 3.发送信号

    [subject sendNext:@"4"];

    [subject sendNext:@"5"];

}


#pragma mark - 测试 RACReplaySubject的使用

- (void)testReplaySubject{

    // 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 = [RACReplaySubjectsubject];

    

    // 2.发送信号

    [replaySubject sendNext:@1];

    [replaySubject sendNext:@2];

    [replaySubject sendNext:@3];

    

    // 3.订阅信号

    [replaySubject subscribeNext:^(id x) {

        

        NSLog(@"第一个订阅者接收到的数据%@",x);

    }];

    

    // 订阅信号

    [replaySubject subscribeNext:^(id x) {

        

        NSLog(@"第二个订阅者接收到的数据%@",x);

    }];

    

    [replaySubject sendNext:@4];

    [replaySubject sendNext:@"5"];

}


#pragma mark - 测试组合 combine

- (void)testCombine{

    // 在必需验证每个所填写的数值符合标准,Button才能点击

#warning enabled会报错  ,暂时原因不明

    RAC(self.signInButton,selected) = [RACSignalcombineLatest:@[self.usernameTextField.rac_textSignal , self.passwordTextField.rac_textSignal]reduce:^id(NSString* username ,NSString* password){

        self.signInButton.enabled = username.length > 6 && password.length >6;

        return@(username.length >6 && password.length >6);

    }];

//    RAC(self,signInButton.titleLabel.text) = [RACSignal combineLatest:@[self.usernameTextField.rac_textSignal , self.passwordTextField.rac_textSignal] reduce:^id(NSString* username , NSString* password){

//        return username;

//        return @(username.length > 6 && password.length > 6);

//    }];

}


/*

 实现一个时钟应用

 实现的逻辑顺序是:设置一个间隔为一秒。从现在开始调用的函数。并把当前实际传入。这个函数返回一个NSString然后把这个NSString和界面上的textField绑定在了一起。从而实现时钟程序。表现了流和绑定响应。

 */

#pragma mark - 实现一个时钟应用

- (void)testClockApplication{

    RAC(self,usernameTextField.text) = [[[RACSignalinterval:1.0onScheduler:[RACSchedulercurrentScheduler]]startWith:[NSDatedate]]map:^id(NSDate* date) {

        NSLog(@"date %@",date);

        NSDateComponents* dateCompenents = [[NSCalendarcurrentCalendar]components:NSCalendarUnitYear |NSCalendarUnitMonth |NSCalendarUnitWeekdayNSCalendarUnitHour |NSCalendarUnitMinute |NSCalendarUnitSecondfromDate:date];

//        NSLog(@"dateCompenents %@",dateCompenents);

        return [NSStringstringWithFormat:@"%02ld:%02ld:%02ld %02ld:%02ld:%02ld",(long)dateCompenents.year,(long)dateCompenents.month,(long)dateCompenents.weekday,(long)dateCompenents.hour,(long)dateCompenents.minute,(long)dateCompenents.second];

    }];

}


#pragma mark - 网络测试.....

#pragma mark - Network Request:这些可以通过自定义信号,也就是RACSubject(继承自RACSignal,可以理解为自由度更高的signal)来搞定

- (void)testNetwork{

    RACSubject* subject = [selfdoRequest];

    [subject subscribeNext:^(NSString * x) {

        [selflogSource:@"testNetwork"text:x];

    }];

}


#pragma mark - 模拟网络请求,1.5秒后得到请求内容

- (RACSubject* )doRequest{

    RACSubject* subject = [RACSubjectsubject];

    // 模拟1.5秒后得到请求内容

    // 只触发1

    // 尽管subscribeNext什么也没做,但如果没有的话map是不会执行的

    // subscribeNext就是定义了一个接收体

    [[[[RACSignalinterval:1.5onScheduler:[RACSchedulermainThreadScheduler]]

       take:1]

      map:^id(NSString* value) {

        [selflogSource:@"Network"text:value];

        [subject sendNext:@"测试"];

        returnnil;

    }]

     subscribeNext:^(NSString* value) {

     }];

    return subject;

}


#pragma mark - 测试常用宏RAC RAC可以看作某个属性的值与一些信号的联动

- (void)testRAC{

//    RAC(self.signInButton,enabled) = [RACSignal combineLatest:@[self.usernameTextField.rac_textSignal , self.passwordTextField.rac_textSignal] reduce:^id(NSString* userName , NSString* password){

//        return @(userName.length >= 6 && password.length >= 6);

//    }];

}


#pragma mark - 测试 RACObserve监听属性的改变,使用blockKVO

- (void)testRACObserve{

//    [self.usernameTextField.rac_textSignal subscribeNext:^(NSString* text) {

//        [self logSource:@"rac_textSignal" text:text];

//    }];

    //只要你的username有变化,都可以打印出来实现了KVO的功能却减少了无数的代码,体现了绑定和响应

    [RACObserve(self, userName)subscribeNext:^(NSString* newName) {

        [selflogSource:@"RACObserve"text:newName];

    }];

    // 使用filter过滤,只响应部分情况

    [[RACObserve(self, userName)filter:^BOOL(NSString* text) {

        return [texthasSuffix:@"2"];//只响应以2结尾的字符串

    }] subscribeNext:^(NSString* text) {

        [selflogSource:@"这是一个以2结尾的字符串"text:text];

    }];

    

    NSTimer *timer = [NSTimertimerWithTimeInterval:1.0target:selfselector:@selector(changeUserName)userInfo:nilrepeats:YES];

    [[NSRunLoopmainRunLoop]addTimer:timerforMode:NSDefaultRunLoopMode];

}


#pragma mark - 测试 RACObserve监听属性的改变,修改监听属性的值

- (void)changeUserName{

    self.userName = [[NSStringalloc]initWithFormat:@"userName %zi",arc4random_uniform(1000)];

}


#pragma mark - 测试 switching switchToLatest:取指定的那个水龙头的吐出的最新玻璃球

- (void)testSwitching{

    RACSubject* letters = [RACSubjectsubject];

    RACSubject* numbers = [RACSubjectsubject];

    RACSubject* signalOfSignals = [RACSubjectsubject];

    RACSignal* switched = [signalOfSignalsswitchToLatest];

    

    // Outputs: A B 1 D

    [switched subscribeNext:^(NSString* text) {

        [selflogSource:@"switched"text:text];

    }];

    

    // 取指定的那个水龙头的吐出的最新玻璃球

    // 设置 letters为最新的水龙头

    [signalOfSignals sendNext:letters];

    [letters sendNext:@"A"];

    [letters sendNext:@"B"];

    // 设置 numbers为最新的水龙头

    [signalOfSignals sendNext:numbers];

    [letters sendNext:@"C"];

    [numbers sendNext:@"1"];

    // 设置 letters为最新的水龙头

    [signalOfSignals sendNext:letters];

    [numbers sendNext:@"2"];

    [letters sendNext:@"D"];

}


#pragma mark - 测试组合 combine  combineLatest:任何时刻取每个水龙头吐出的最新的那个玻璃球

- (void)testCombining{

    RACSubject* letters = [RACSubjectsubject];

    RACSubject* numbers = [RACSubjectsubject];

    RACSignal* combine = [RACSignalcombineLatest:@[letters , numbers]reduce:^(NSString* letter ,NSString* number){

        return [letterstringByAppendingString:number];

    }];

    

    // Outputs: B1 B2 C2 C3

    [combine subscribeNext:^(NSString* x) {

        [selflogSource:@"combine"text:x];

    }];

    //任何时刻取每个水龙头吐出的最新的那个玻璃球 , A的值被B覆盖了

    [letters sendNext:@"A"];

    [letters sendNext:@"B"];

    [numbers sendNext:@"1"];

    [numbers sendNext:@"2"];

    [letters sendNext:@"C"];

    [numbers sendNext:@"3"];

}


#pragma mark - 测试 map and flatten

- (void)testMapAndFlatten{

    // map flatten

    [selftestFlattenMap];

}


#pragma mark - 测试 map and flatten  - map flatten

- (void)testFlattenMap{

    RACSequence* numbers =self.dataArrNumber.rac_sequence;

    

    // Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

    RACSequence* extended = [numbersflattenMap:^RACStream *(NSString* num) {

        return@[num,num].rac_sequence;

    }];

    

    // Contains: 1_ 3_ 5_ 7_ 9_

    RACSequence* edited = [numbersflattenMap:^RACStream *(NSString* num) {

        if (num.intValue %2 ==0){

//            [self logSource:@"num.intValue" text:num];

            return [RACSequenceempty];

        }else{

            NSString* newNum = [numstringByAppendingString:@"_"];

//            [self logSource:@"newNum" text:newNum];

            return [RACSequencereturn:newNum];

        }

    }];

    // 排序 按照顺序输出

    RACSequence* queue =@[extended,edited].rac_sequence;

    [[queue flatten].signalsubscribeNext:^(NSString* text) {

        [selflogSource:@"queue"text:text];

    }];

    

    // 输出顺序会乱

//    [[extended signal] subscribeNext:^(NSString* text) {

//        [self logSource:@"extended" text:text];

//    }];

//    [edited.signal subscribeNext:^(NSString* text) {

//        [self logSource:@"edited" text:text];

//    }];

}


#pragma mark - 测试 flatten

- (void)testFlatten{

    // 测试flatten起连接作用类似于cancat

//    [self testFlattenForConcat];

    

    // Signals are mergedmerge可以理解成把几个水管的龙头合并成一个,哪个水管中的玻璃球哪个先到先吐哪个玻璃球)

    RACSubject* letters = [RACSubjectsubject];

    RACSubject* numbers = [RACSubjectsubject];

    RACSignal* signalOfSignals = [RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        [subscriber sendNext:letters];

        [subscriber sendNext:numbers];

        [subscriber sendCompleted];

        returnnil;

    }];

    

    // flatten合并

    RACSignal* flattened = [signalOfSignalsflatten];

    // Outputs: A 1 B C 2

    [flattened subscribeNext:^(NSString* x) {

        [selflogSource:@"flattened"text:x];

    }];

    

    // merging合并

    RACSignal* merged = [RACSignalmerge:@[letters , numbers]];

    // Outputs: A 1 B C 2

    [merged subscribeNext:^(NSString* x) {

        [selflogSource:@"merged"text:x];

    }];

    

    [letters sendNext:@"A"];

    [numbers sendNext:@"1"];

    [letters sendNext:@"B"];

    [letters sendNext:@"C"];

    [numbers sendNext:@"2"];

}


#pragma mark - 测试 flatten concat

- (void)testFlattenForConcat{

    // Sequences are concatenated :序列是连接

    RACSequence *letters =self.dataArrString.rac_sequence;

    RACSequence *numbers =self.dataArrNumber.rac_sequence;

    RACSequence *sequenceOfSequences =@[letters,numbers].rac_sequence;

    

    // Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9

    // flatten在此起连接作用

    RACSequence *flattened=[sequenceOfSequencesflatten];

    [flattened.signalsubscribeNext:^(NSString* text) {

        [selflogSource:@"flatten"text:text];

    }];

}


#pragma mark - 输出字符串

- (void)logSource:(NSString* )source text:(NSString* )text{

    NSLog(@"%@ %@",source,text);

}


#pragma mark - 测试 concat:把一个水管拼接到另一个水管之后

- (void)testConcat{

    RACSequence* letters =self.dataArrString.rac_sequence;

    RACSequence* numbers =self.dataArrNumber.rac_sequence;

    

    RACSequence* concated = [lettersconcat:numbers];

    

    // 输出 Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9

    [concated.signalsubscribeNext:^(NSString* text) {

        [selflogSource:@"concat"text:text];

    }];

}


#pragma mark - 测试 filter:过滤,不符合要求的玻璃球不允许通过

- (void)testFilter{

    RACSequence* numbers =self.dataArrNumber.rac_sequence;

    

    // 转换后Contains: 2 4 6 8

    [[numbers filter:^BOOL(NSString* value) {

        return (value.intValue %2) ==0;

    }].signalsubscribeNext:^(NSString* x) {

        [selflogSource:@"filter"text:x];

    }];

}


#pragma mark - 测试 map:映射,可以看做对玻璃球的变换、重新组装

- (void)testMap{

    RACSequence* letters =self.dataArrString.rac_sequence;

    RACSequence* mapped = [lettersmap:^id(NSString* value) {

        [selflogSource:@"map"text:value];

        // 转换后Contains: AA BB CC DD EE FF GG HH II

        return [valuestringByAppendingString:value];

    }];

    [mapped.signalsubscribeNext:^(NSString* x) {

        [selflogSource:@"map"text:x];

    }];

}


#pragma mark - 测试注入效果 -doNext: -doError: -doCompleted:

- (void)testNextAndCompleted{

    __blockunsigned subscriptions=0;

    RACSignal* loggingSignal = [RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

        subscriptions ++;

        [subscriber sendNext:@"eee"];

        [subscriber sendCompleted];

        returnnil;

    }];

    

    // 不输出

    [loggingSignal doCompleted:^{

        NSLog(@"about to complete subscription %u",subscriptions);

    }];

    // 当有其它地方调用 loggingSignal时会调用

    loggingSignal = [loggingSignal doCompleted:^{

        NSLog(@"about to complete subscription %u",subscriptions);

    }];

    // 输出

    [loggingSignal subscribeCompleted:^{

        NSLog(@"subscription %u",subscriptions);

    }];

    

    [loggingSignal subscribeNext:^(id x) {

        NSLog(@"x %@",x);

    }];

}



0 0
原创粉丝点击