美文网首页IOS知识积累
iOS - RAC(ReactiveObjC)的简单介绍

iOS - RAC(ReactiveObjC)的简单介绍

作者: ShIwEn9 | 来源:发表于2019-09-27 09:48 被阅读0次

    本编文章是我对 MVVM + RAC 知识的一些学习总结了分享,如错误的地方,希望可以各位大佬积极指正。⛽️
    文章中引用了别人文章中很多的内容,在这里说明一下侵权删。
    打一个广告:本人目前离职状态(公司资金出现问题,原部门人员全体被迫离职。。。)如果有对我感兴趣的请给我留言或者简书私聊我,谢谢 !

    ReactiveCocoa

    在学习RAC之前我们得了解一下什么是响应式编程(FRP: functional reactive programming 翻墙),因为RAC就是通过 函数式 + 响应式编程相结合实现的。

    🌰 在我们编写代码的时候如:a = b + c 当我们更改 b 或 c 的时候,a 的值并不会立即改变,最起码你得重写运行一次代码才行,但是在响应式编程的世界里面,我们 改变 b 或者 c 的时候,a 的值也会发生改变,这就意味着它们之间出现了一只绑定的关系。是不是很神奇。。。。。。

    一、配置RAC

    在实际使用中,我们并不是直接查询ReactiveCocoa,它其实是一个总称,在ReactiveCocoa中分为Swift版的 ReactiveSwift和OC版本的
    ReactiveObjC,这里我介绍的是ReactiveObjC部分。
    使用cocoaPods在podfile中添加

     pod 'ReactiveObjC', '~> 3.1.0' //或者 pod 'ReactiveObjC'
    

    然后pod install一下。在项目中#import <ReactiveObjC.h>,建议放入pch头文件中。

    二、RAC的一些简单介绍

    RAC的一个主要优点是,它提供了处理异步行为的单一、统一的方法,包括委托方法、回调块、目标操作机制、通知和kvo

    1. 基本类的使用
    • RACSignal类的使用
    // RACSignal的使用
    -(void)RACSignal {
        // 1.创建signal信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            NSLog(@"%@",subscriber);
            // subscriber并不是一个对象
            // 3. 发送信号
            [subscriber sendNext:@"123"];
            
            // 4. 完成订阅
            [subscriber sendCompleted];
            
            // 4. 销毁信号
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"销毁了");
            }] ;
        }];
        
        // 2.1订阅
        [signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        // 2.2 针对实际中可能出现的逻辑错误,RAC提供了订阅error信号
        [signal subscribeError:^(NSError * _Nullable error) {
            NSLog(@"%@",error);
        }];
    }
    

    RACSignal 是一个信号类,只要有数据改变就会把数据包装成信号传递出去,但是注意发出信号的并不是 RACSignal 它不可以发送信号,发送信号的是 订阅者 subscriber ,并把nextBlock保存到订阅者里面,创建的时候会返回 [RACDynamicSignal createSignal:didSubscribe];调用RACDynamicSignal的didSubscribe,发送信号[subscriber sendNext:value],拿到订阅者的nextBlock调用。


    • RACSubjec类的使用
    // RACSubject(可自己发送信号也可以自己充当信号)
    - (void)initRACSubject {
        /*
         1.创建RACSubject信号
         2.订阅信号(subscribeNext)。本质就是创建订阅者,把订阅者放到数组里面。
         3.发送信号(sendNext)。本质就是让订阅者数组里面的每一个订阅者都去调用 sendNext 方法。
         */
        
        RACSubject *subject = [RACSubject subject];
        [subject subscribeNext:^(id  _Nullable x) {
            NSLog(@"RACSubjectTest x = %@",x);
        } error:^(NSError * _Nullable error) {
            NSLog(@"%@",error);
        } completed:^{
            NSLog(@"11111");
        }];
        //发送信号
        [subject sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"];
    }
    

    RACSubject最大的优点就是自己可以发送和接收信息,同时它是RACSignal的子类。在公司的项目我发现很多的时候都会用RACSubject来代替RACSignal作为返回值。

    • RACReplaySubject类的使用
    // RACReplaySubject
    -(void)initRACReplaySubject{
        // 1.1创建方法一
        RACReplaySubject *repSubject = [RACReplaySubject subject];
        
        // 1.2创建方法二: 通过设置capacity来限定它接收事件的数量
        RACReplaySubject *repSubject2 = [RACReplaySubject replaySubjectWithCapacity:2];
        
        // 2发送
        [repSubject sendNext:@"repSubject"];
        
        // 这里限制了repSubject2的最大接受事件为2 所以第一个会被移除
        [repSubject2 sendNext:@"repSubject2"];
        [repSubject2 sendNext:@"repSubject22"];
        [repSubject2 sendNext:@"repSubject222"];
        
        // 3订阅
        [repSubject subscribeNext:^(id  _Nullable x) {
            NSLog(@"repSubject = %@",x);
        }];
        
        [repSubject2 subscribeNext:^(id  _Nullable x) {
            NSLog(@"repSubject2 = %@",x);
        }];
    }
    

    RACReplaySubject作为RACSubject的子类自然会拥有父类的所有特性,同时它也有自己的特性,比如我们通过 replaySubjectWithCapacity 去创建RACReplaySubject那么就会限制它接收时间的数量。在它的内部,通过遍历来实现限制功能。

    • RACTuple(元组)的使用
      RACTuple内部就是封装了数组,用起来跟数组差不多,用过swift的小伙伴估计都知道元组的概念,在这就不多说了。
    // RACTuple(元组)-- 其内部就是封装了数组,用起来跟数组差不多
    -(void)initRACTuple{
        //1.创建
        // 1.1 通过定值创建RACTuple
        RACTuple *tuple = [RACTuple tupleWithObjects:@"1",@"2",@"3", nil];
        
        // 1.2 利用 RAC 宏快速封装
        RACTuple *tuple2 = RACTuplePack(@"1", @"2", @"3");
        
        // 1.3 从别的数组中获取内容
        RACTuple *tuple3 = [RACTuple tupleWithObjectsFromArray:@[@"1",@"2",@"3"]];
        
        // 添加某个值
        [tuple tupleByAddingObject:@"123"];
        NSLog(@"tupleByAddingObject = %@", tuple);
        
        // 根据下标获取
        NSLog(@"tuple objectAtIndex:2 = %@",[tuple objectAtIndex:2]);
        
        // 取出所有的值
        NSLog(@"[tuple2 allObjects] = %@",[tuple2 allObjects]);
        
        NSLog(@"元组-->%@", tuple3[0]);
        NSLog(@"第一个元素-->%@", [tuple3 first]);
        NSLog(@"最后一个元素-->%@", [tuple3 last]);
    }
    

    其内部也实现了一些方法,在例子中都有展示。

    • RACCommand类的使用
      这个类比较的复杂,我准备后面单独的拿出详细的研究一下。这里就简单的介绍一下使用了。
    //RACCommand
    -(void)RACCommand{
        /*
         command 命令
         可以监听信号的状态等,可以在订阅/执行(即 excute:方法)时传递参数。因此当需要向信号传递参数的时候,RACComand 更好用
         RACCommand 包含了一个 executionSignal 的信号,这个信号是对用户透明的,它是自动创建的,由 RACCommand 进行管理。许多资料中把它称之为信号中的信号,是因为这个信号会发送其它信号——即 RACCommand 在初始化的 signalBlock 中创建(return)的信号。这个信号是 RACCommand 创建时由我们创建的,一般是用于处理一些异步操作,比如网络请求等。
         RACCommand 的另一个优点了,它可以监听 RACCommand 自身的执行状态,比如开始、进行中、完成、错误等。用 RACSignal 可以监听到完成(complete)、错误(error)、进行中(next)。但开始就无法实现了.
         */
        //NSString *input = @"执行";
        
        RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            // execute发送的值
            NSLog(@"input = %@",input);
            
            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                
                // RACSignal 发出信号
                [subscriber sendNext:@"RACSignal : 我是谁,我在哪?"];
                [subscriber sendCompleted];
                return [RACDisposable disposableWithBlock:^{
                    // block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
                    // 执行完Block后,当前信号就不再被订阅了。
                    // 信号销毁的时候 会执行这个闭包
                    // 用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
                    // 使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。
                    NSLog(@"signal1销毁了");
                }];
            }];
        }];
        
        //订阅外层信号(即 executionSignals)。外层信号在订阅或执行(即 execute: )时发送。因此我们可以将它视作请求即将开始之前的信号;
        //订阅内层信号,因为内层信号由外层信号(executionSignals)作为数据发送(sendNext:),而发送的数据一般是作为 subcribeNext:时的 block 的参数来接收的,因此在这个块中,块的参数就是内层信号。这样我们就可以订阅内层信号了
        // executionSignals就是用来发送信号的信号源,需要注意的是这个方法一定要在执行execute方法之前,否则就不起作用了
        [command.executionSignals subscribeNext:^(RACSignal *_Nullable x) {
            [x subscribeNext:^(id  _Nullable x) {
                NSLog(@"executionSignals-->subscribeNext-->%@",x);
            }];
            NSLog(@"executionSignals - x = %@",x);
        }];
        
        /*
          获取信号中信号最近发出信号,订阅最近发出的信号
          注意switchToLatest:只能用于信号中的信号
         */
        [[command.executionSignals switchToLatest] subscribeNext:^(id  _Nullable x) {
            NSLog(@"switchToLatest-->%@",x);
        }];
        // 写法二 用点语法
    //    [command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
    //        <#code#>
    //    }];
        
        // 监听信号是否执行完毕
        [[command.executing skip:1] subscribeNext:^(id  _Nullable x) {
            NSLog(@"executing-->%@",x);
            if ([x boolValue]) {
                NSLog(@"正在执行");
            }else{
                NSLog(@"执行完毕");
            }
        }];
        
        //RACCommand 比较特殊的一点是 error 信号需要在 errors 中订阅,而不能在 executionSignals 中订阅。在这里我们订阅了 errors 信号,并修改 data、error 和 requestStatus 属性值。
        [command.errors subscribeNext:^(id  _Nullable x) {
            NSLog(@"errors-->%@",x);
        }];
        
        //开始执行
        [command execute:@"起飞了"];
        
    }
    

    简单来说就是对信号进行管理和监听。

    2. RAC定义的常用方法
    • 按钮事件通过RAC来实现
    // 点击事件用RAC实现
    - (void)RACButton_targetAction {
    // 按钮的点击事件
        [[self.testButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
            NSLog(@"RAC按钮点击了");
            NSLog(@"%@",x);
        }];
    // tap 点击事件
        self.testLable.userInteractionEnabled = YES;
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
        [self.testLable addGestureRecognizer:tap];
        [tap.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
            //点击事件响应的逻辑
            NSLog(@"RAC的Label点击了");
            NSLog(@"%@",x);
        }];
    }
    
    • KVO的实现
    - (void)RAC_KVO {
        //1
        [RACObserve(self.testLable, text) subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        //2
        [RACObserve(self.testTextField, text) subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        } error:^(NSError * _Nullable error) {
            NSLog(@"%@",error);
        } completed:^{
            NSLog(@"success");
        }];
    }
    
    • RAC实现代理
    // RAC实现代理
    - (void)RACTextFieldDelegate {
        [[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext:^(RACTuple * _Nullable x) {
            NSLog(@"textField delegate == %@",x);
        }];
        self.testTextField.delegate = self;
    }
    
    • RAC 实现通知
    // RAC实现通知
    - (void)RACNotification {
        [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidHideNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
            NSLog(@"%@",x);
        }];
    }
    
    • RAC实现定时
    - (void)RACTimer {
        //主线程中每两秒执行一次
        [[RACSignal interval:2.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
            NSLog(@"%@",x);
        }];
        //创建一个新线程
        [[RACSignal interval:1 onScheduler:[RACScheduler schedulerWithPriority:(RACSchedulerPriorityHigh) name:@" com.ReactiveCocoa.RACScheduler.mainThreadScheduler"]] subscribeNext:^(NSDate * _Nullable x) {
            
            NSLog(@"%@",[NSThread currentThread]);
        }];
    }
    
    • RAC 实现遍历
    - (void)RACSequence {
        //遍历数组
        NSArray *racAry = @[@"rac1",@"rac2",@"rac3"];
        [racAry.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        //遍历字典
        NSDictionary *dict = @{@"name":@"dragon",@"type":@"fire",@"age":@"1000"};
        [dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            RACTwoTuple *tuple = (RACTwoTuple *)x;
            NSLog(@"key == %@, value == %@",tuple[0],tuple[1]);
        }];
    }
    
    3. 相关函数的使用
    • 映射相关
      映射在项目中经常会被用到,映射方法是将原信号中的内容映射成新的指定内容。
      通过对比,从map的实现方法中可以看出是基于flattenMap方法的一层封装,但同时又有不同之处。
      • flattenMap 映射
        flattenMap作用:把原信号的内容映射成一个新信号,并return返回给一个RACStream类型数据。实际上是根据前一个信号传递进来的参数重新建立了一个信号,这个参数,可能会在创建信号的时候用到,也有可能用不到。
    - (void)FlattenMap {
        // flattenMap
        [[self.testTextField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
            //自定义返回内容
            return [RACReturnSignal return:[NSString stringWithFormat:@"flattenMap 自定义了返回信号:%@",value]];
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"x = %@",x);
            NSLog(@"%@",NSStringFromClass([x class]));
        }];
    }
    
    • Map 映射
      map作用:是将原信号的值自定义为新的值,不需要再返回RACStream类型,value为源信号的内容,将value处理好的内容直接返回即可。map方法将会创建一个一模一样的信号,只修改其value。
    // map 和 flattenMap 映射
    - (void)map {    
        [[self.testTextField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
            return [NSString stringWithFormat:@"map 自定义了返回信号:%@",value];
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"x = %@",x);
            NSLog(@"%@",NSStringFromClass([x class]));
        }];
    }
    

    同样作为映射命令,在实际开发过程中,如果使用map命令,则block代码块中return的是对象类型;而flattenMap命令block代码块中return的是一个新的信号。

    • 过滤相关
      • skip实现过滤
        忽略前几个信号值
    -(void)RACSkip{
        RACSubject *subject = [RACSubject subject];
        
        [[subject skip:1] subscribeNext:^(id  _Nullable x) {
            NSLog(@"skip = %@",x);
        }];
        
        [subject sendNext:@"12"];
        [subject sendNext:@"123"];
        [subject sendNext:@"1234"];
        
    }
    

    得到的信号值 只有 123 和 1234,在开发中经常会用它来过滤第一个原始值。

    • filter实现过滤
      通过return一个BOOL值来判断是否过滤掉信号。
    - (void)RACfilter {
        @weakify(self);
        [[self.testTextField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
            //过滤判断条件
            @strongify(self)
            if (self.testTextField.text.length >= 6) {
                self.testTextField.text = [self.testTextField.text substringToIndex:6];
                self.testLable.text = @"已经到6位了";
                self.testLable.textColor = [UIColor redColor];
            }else{
                self.testLable.text = @"最多输入6位数";
                self.testLable.textColor = [UIColor greenColor];
            }
            return value.length <= 6;
            
        }] subscribeNext:^(NSString * _Nullable x) {
            //订阅逻辑区域
            NSLog(@"filter过滤后的订阅内容:%@",x);
        }];
    }
    
    • ignore实现过滤
    // ignore实现过滤
    - (void)ignoreValue {
       [[self.testTextField.rac_textSignal ignoreValues] subscribeNext:^(id  _Nullable x) {
           //将self.testTextField的所有textSignal全部过滤掉
           NSLog(@"过滤后%@",x);
       }];
       
       [[self.testTextField.rac_textSignal ignore:@"1"] subscribeNext:^(id  _Nullable x) {
           //将self.testTextField的textSignal中字符串为指定条件的信号过滤掉
           NSLog(@"过滤 1 后%@",x);
       }];
    }
    
    • distinctUntilChanged 实现过滤
      判断当前信号的值跟上一次的值相同,若相同时将不会收到订阅信号
    - (void)distinctUntilChanged {
        // 第一种:不做distinctUntilChanged限制的
        // 1. 创建
        RACSubject *subject = [RACSubject subject];
        
        // 2.1订阅
        [subject subscribeNext:^(id  _Nullable x) {
            NSLog(@"RACSubjectTest x = %@",x);
        } error:^(NSError * _Nullable error) {
            NSLog(@"%@",error);
        } completed:^{
            NSLog(@"11111");
        }];
        //3.发送信号
        [subject sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"];
        [subject sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"];
        
        
        //第二种:通过 distinctUntilChanged 方法来 判断前面信号的值是否和当前的相同,如果相同就不会去接受信号
        // 1. 创建
        RACSubject *subject2 = [RACSubject subject];
        // 2.1订阅
        [[subject2 distinctUntilChanged] subscribeNext:^(id  _Nullable x) {
            NSLog(@"RACSubjectTest distinctUntilChanged x = %@",x);
        }];
        // 2.2错误
        [subject subscribeError:^(NSError * _Nullable error) {
            NSLog(@"error = %@",error);
        }];
        
        // 3.发送
        [subject2 sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"]; // 1
        [subject2 sendNext:@"信号🍺🍺🍺🍺🍺🍺🍺"]; // 2
        [subject2 sendNext:@"信号🍺🍺🍺"];          // 3
    }
    

    打印的结果只有 1 、3

    • 链接、合并信号相关
      • concat连接信号
        特点:concat组合信号,让信号按照顺序去执行。
        使用场景:假如我们现在有这么一个需求:有两个网络请求,我们需要先请求第一个,拿到第一个返回的数据后,再去执行第二个网络请求。这时,我们就可以用到concat了。
        注意点:当你使用concat 连接信号的时候,第一个信号一定要执行sendCompleted方法,不然永远不用执行第二个信号。
    -(void)RACConcat{
        // 创建信号1
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"A123"];
            //一定要执行sendCompleted,不然永远都不会执行请求B
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"A 销毁了");
            }];
        }];
        
        [signalA subscribeNext:^(id  _Nullable x) {
            NSLog(@"A = %@",x);
        }];
        
        RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"B1234"];
            //一定要执行sendCompleted,不然永远都不会执行请求B
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"B 销毁了");
            }];
        }];
        
        [signalB subscribeNext:^(id  _Nullable x) {
            NSLog(@"B = %@",x);
        }];
        
        // concat:按照顺序去连接,先执行请求A,再去执行请求B
        RACSignal *concatSignal = [signalA concat:signalB];
        // 订阅组合信号
        [concatSignal subscribeNext:^(id  _Nullable x) {
            // 既能拿到A信号的值,又能拿到B信号的值
            NSLog(@"A和B = %@",x);
        }];
    }
     /*
         2019-09-04 12:06:50.773126+0800 ReactiveObjc[3070:463032] A = A123
         2019-09-04 12:06:50.773196+0800 ReactiveObjc[3070:463032] A 销毁了
         2019-09-04 12:06:50.773226+0800 ReactiveObjc[3070:463032] B = B1234
         2019-09-04 12:06:50.773241+0800 ReactiveObjc[3070:463032] B 销毁了
         2019-09-04 12:06:50.773550+0800 ReactiveObjc[3070:463032] A和B = A123
         2019-09-04 12:06:50.773572+0800 ReactiveObjc[3070:463032] A和B = B1234
         2019-09-04 12:06:50.773585+0800 ReactiveObjc[3070:463032] B 销毁了
         2019-09-04 12:06:50.773595+0800 ReactiveObjc[3070:463032] A 销毁了
         */
    
    • then 连接信号
      then也是用于连接两个信号,当第一个信号完成,才会连接then返回的信号,then底层也是使用了concat实现
    -(void)RACThen{
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            // 发送信息
            [subscriber sendNext:@"signalA"];
            // 发送完成,不然接受不到B的数据
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"signalA dealloc");
            }];
        }];
        
        [signalA subscribeNext:^(id  _Nullable x) {
            NSLog(@"signalA = %@",x);
        }];
        
        RACSignal *signalB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            // 发送信息
            [subscriber sendNext:@"signalB"];
            [subscriber sendCompleted];
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"signalB dealloc");
            }];
        }];
        
        [signalB subscribeNext:^(id  _Nullable x) {
            NSLog(@"signalB = %@",x);
        }];
        // 利用then组合信号
        // then:会忽略掉 A 信号的所有值 之后再去接收 B 的值
        RACSignal *thenSignal = [signalA then:^RACSignal * _Nonnull{
            // 返回 B 的信号
            return signalB;
        }];
        
        [thenSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@" signalB = %@",x);
        }];
        
        /*
         2019-09-04 13:57:00.316571+0800 ReactiveObjc[3551:500030] signalA = signalA
         2019-09-04 13:57:00.316647+0800 ReactiveObjc[3551:500030] signalA dealloc
         2019-09-04 13:57:00.316692+0800 ReactiveObjc[3551:500030] signalB = signalB
         2019-09-04 13:57:00.316714+0800 ReactiveObjc[3551:500030] signalB dealloc
         2019-09-04 13:57:00.318309+0800 ReactiveObjc[3551:500030]  x = signalB
         2019-09-04 13:57:00.318333+0800 ReactiveObjc[3551:500030] signalB dealloc
         2019-09-04 13:57:00.318349+0800 ReactiveObjc[3551:500030] signalA dealloc
         */
    }
    
    • merge 合并信号
      merge把两个信号合并为一个信号,任何一个信号有新值的时候就会调用.
    -(void)RACMerge{
        RACSubject *subjectA = [RACSubject subject];
        
        RACSubject *subjectB = [RACSubject subject];
        
        RACSignal *merge = [subjectA merge:subjectB];
        
        // 订阅信号
        [merge subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        // 任何一个信号有新的值的时候就会调用,谁先发请求谁先响应
        [subjectA sendNext:@"发送A请求"];
        [subjectB sendNext:@"发送B请求"];
        /*
         2019-09-04 14:13:10.027433+0800 ReactiveObjc[3574:503914] 发送A请求
         2019-09-04 14:13:10.027487+0800 ReactiveObjc[3574:503914] 发送B请求
         */
    }
    
    • ZipWith 合并信号
      把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,并且把两个信号的内容合并成一个元组,才会触发事件
      当一个界面有多个网络请求时,要等所有请求完成才更新UI,这时我们就可以用zipWith
      注意:只有当两个请求都发送请求之后才会触发回调事件
    -(void)RACZipWith{
        RACSubject *subjectA = [RACSubject subject];
        
        RACSubject *subjectB = [RACSubject subject];
        
        RACSignal *zipWith = [subjectA zipWith:subjectB];
        
        [zipWith subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        // RACTwoTupl继承自 RACTuple RACTuple 类似于数组
        [zipWith subscribeNext:^(RACTwoTuple *  _Nullable x) {
            NSLog(@"%@",[x objectAtIndex:0]);
        }];
        //只有当两个请求都发送请求之后才会触发回调事件
        [subjectB sendNext:@"send B request"];
        [subjectA sendNext:@"send A request"];
        
        //上面我们明明就是先调用 signalB 的sendNext 方法,但回调打印的却是signalA在前面。这是为什么?因为我们在组合信号的时候 [signalA zipWith:signalB],我们把signalA放前面,所以回调的时候它也会放在前面。
        /*
         <RACTwoTuple: 0x28252bb20> (
         "send A request",
         "send B request"
         )
         */
    }
    
    • combineLatest 和 Reduce 合并信号
      这我感觉也是项目中合并信号用到最多的一个函数式了
    🌰
    -(void)combineLatestAndReduce{
        
        CGFloat width = self.view.bounds.size.width;
        //账号
        UITextField *tf1 = [[UITextField alloc]initWithFrame:CGRectMake(width / 2.0 - 100, 380, 200, 40)];
        tf1.placeholder = @"请输入账号";
        tf1.backgroundColor = [UIColor colorWithRed:0.0f green:0.5f blue:0.8f alpha:1.0f];
        [self.view addSubview:tf1];
        //密码
        UITextField *tf2 = [[UITextField alloc]initWithFrame:CGRectMake(width / 2.0 - 100, 450, 200, 40)];
        tf2.placeholder = @"请输入密码";
        tf2.backgroundColor = [UIColor colorWithRed:0.0f green:0.5f blue:0.8f alpha:1.0f];
        [self.view addSubview:tf2];
        
        
        //登陆按钮
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = CGRectMake(width / 2.0 - 100, 580, 200, 40);
        [button setTitle:@"登录" forState:UIControlStateNormal];
        [button setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
        button.titleLabel.font = [UIFont systemFontOfSize:15];
        [button setBackgroundColor:[UIColor greenColor]];
        //@weakify(self);
        [[button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
            //@strongify(self);
            NSLog(@"登录");
        }];
        [self.view addSubview:button];
        
        //组合信号
        // 第一个参数:组合哪些信号
        // 第二个参数:reduce 聚合
        // 注意,reduce是一个block,它看起来好像没参数,但其实它的参数是由我们自己来决定的,我们可以自己去修改
        // reduce 参数: 跟组合的信号有关,一一对应,你有几个信号,就有几个参数,所以我们在下面加上(NSString *account, NSString *pwd)
        // reduce 参数类型: 组合的信号发送的是什么类型,参数就是什么类型
        RACSignal *combineSignal = [RACSignal combineLatest:@[tf1.rac_textSignal,tf2.rac_textSignal] reduce:^id _Nullable(NSString *account, NSString *pwd){
            // block调用: 只要任意一个源信号发送内容就会调用,组合成一个新的值
            return @(account.length && pwd.length);
        }];
        
    //    [combineSignal subscribeNext:^(id  _Nullable x) {
    //        NSLog(@"%@",x);
    //        button.enabled = [x boolValue];
    //    }];
        
        
        //上面的写法其实可以换成下面这种,更简单明了
        // RAC 用于给某个对象的某个属性绑定
        RAC(button,enabled) = combineSignal;
    }
    

    到这里,RAC的一些基本使用就介绍完了,当然知其然要知其所以然,阅读源码,了解人家实现的思路才是王道。各位骚年们,我们一起努力吧!!⛽️

    相关文章

      网友评论

        本文标题:iOS - RAC(ReactiveObjC)的简单介绍

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