iOS - RAC(ReactiveObjC)的简单介绍

本编文章是我对 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的一些基本使用就介绍完了,当然知其然要知其所以然,阅读源码,了解人家实现的思路才是王道。各位骚年们,我们一起努力吧!!⛽️

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,029评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,395评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,570评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,535评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,650评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,850评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,006评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,747评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,207评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,536评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,683评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,342评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,964评论 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,772评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,004评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,401评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,566评论 2 349

推荐阅读更多精彩内容