RAC

480 阅读17分钟

Rac是个响应式框架,又称函数响应式编程。

RACSignal

    //创建一个信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //发送信号
        [subscriber sendNext:@"hello"];
        return nil;
    }];
    
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];

源码解读

可以当做block理解,订阅就是定义一个block,发送信号就是调用block image.png

RACSubject

// RACSubject继承自RACSignal,但是对历史值不关心
//创建信号
RACSubject *subject = [[RACSubject alloc] init];
//订阅信号
[subject subscribeNext:^(id  _Nullable x) {
     NSLog(@"%@",x);
}];
//发送信号
[subject sendNext:@"test1"];
[subject sendNext:@"test2"];

2021-07-01 09:24:06.934275+0800 ReactCoCoaDemo[83934:2200994] test1
2021-07-01 09:24:06.934353+0800 ReactCoCoaDemo[83934:2200994] test2

RACSubject 既可以发送信号也可以订阅信号


@implementation TGView

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    [self.subject sendNext:@"上班了"];
}

#pragma mark - lazy

- (RACSubject *)subject {
    if (_subject == nil) {
        _subject = [[RACSubject alloc] init];
    }
    return _subject;
}

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self setupUI];
}

- (void)setupUI {
    [self.actionView.subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
}

@end

switchToLatest 最新信号

    RACSubject * signalOfSignal = [RACSubject subject];
    RACSubject * signal1 = [RACSubject subject];
    RACSubject * signal2 = [RACSubject subject];
    RACSubject * signal3 = [RACSubject subject];
    //switchToLatest :最新的信号!!
    [signalOfSignal.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    
    //发送信号
    [signalOfSignal sendNext:signal1];
    [signalOfSignal sendNext:signal2];
    [signalOfSignal sendNext:signal3];
    //发送数据
    [signal3 sendNext:@"3"];
    [signal2 sendNext:@"2"];
    [signal1 sendNext:@"1"];
    
    //最后发送的信号是signal3 ,所以值是3
    2021-07-01 15:21:43.184452+0800 ReactCoCoaDemo[88745:2454741] 3

RACDisposable

    /*
当订阅者发送了sendCompleted 或senfError 就'相当于'结束订阅了, 可以理解成订阅者就销毁了
当订阅者没有被全局变量引用时, sendNext之后, 就'相当于'结束订阅了, 可以理解成订阅者就销毁了
当订阅者被销毁后,不会再发送信号
*/
  //1、创建一个信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //3、发送信号
        //subscriber 发送
        [subscriber sendNext:@"发送信号"];
        _subscriber = subscriber;//可以用self.,强引用一下subscriber就不会走下边了,不强引用着subscriber 的话subscriber在发送完之后就没了就 自动取消订阅了
        //返回值的类型RACDisposable
        //RACDisposable可以帮助我们取消订阅:信号发送完毕或者失败了。(就像通知的注册和注销)
        return [RACDisposable disposableWithBlock:^{
            //清空资源
            NSLog(@"到这了");
        }];
    }];
    
    //2、订阅信号
    RACDisposable *disposable = [signal subscribeNext:^(id  _Nullable x) {
        //x就是信号发送的内容
        NSLog(@"订阅的信号是: %@",x);
        
    }];
    [disposable dispose];//手动 取消订阅

    //信号发送完毕了 默认就会取消订阅
    //只要订阅者在就不会主动取消订阅 如上边强引用着 subscriber 此时可以手动取消订阅

RAC_liftselecor

合并多个信号一次发送

- (void)setupData {
    //请求1
    RACSignal * signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //发送请求
        NSLog(@"请求网络数据 1");
        //发送数据
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"数据1 来了"];
        });
        return nil;
    }];
    
    //请求2
    RACSignal * signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //发送请求
        NSLog(@"请求网络数据 2");
        //发送数据
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            [subscriber sendNext:@"数据2 来了"];
        });
        return nil;
    }];
    
    //数组:存放信号
    //当数组中的所有洗好都发送了数据,才会执行Selector
    //方法的参数:必须和数组的信号一一对应!!
    //方法的参数:就是每一个信号发送的数据!!
    [self rac_liftSelector:@selector(updateUIWithOneData:TwoData:) withSignalsFromArray:@[signal1,signal2]];
}

- (void)updateUIWithOneData:(id )oneData TwoData:(id )twoData {
    NSLog(@"%@",[NSThread currentThread]);
    //拿到数据更新UI
    NSLog(@"UI!!%@%@",oneData,twoData);
}

RAC宏

RAC宏作用: 给对象属性绑定信号

//当textField内容发生变化时,就把内容赋值给label的text属性
RAC(_label,text) = _textField.rac_textSignal;

//输入框背景色绑定了映射后的validUserNameSignal信号,信号变化时背景色更新
RAC(self.userNameTxtField,backgroundColor) = [validUserNameSignal map:^id _Nullable(NSNumber *userNameValid) {
    return [userNameValid boolValue] ? [UIColor whiteColor] : [UIColor yellowColor];
}];

RACObserve作用: 监听某个对象的属性

//当self.label里面的内容发生变化时,就会发送信号
[RACObserve(self.label, text) subscribeNext:^(id  _Nullable x) {
     NSLog(@"%@",x);
 }];

RACTuplePack与RACTupleUnpack

  1. RACTuplePack:将数据封装成元组
  2. RACTupleUnpack:将元组解包为数据
    //使用RACTuplePack封装元组
    RACTuple *racTuple = RACTuplePack(@"字符串1",@"字符串2");
    NSLog(@"测试racTuple:%@",racTuple);

    //使用RACTupleUnpack解元组
    RACTupleUnpack(NSString *str1,NSString *str2) = racTuple;
    NSLog(@"测试RACTupleUnpack:%@-%@",str1,str2);
    
    2021-07-03 14:46:26.456075+0800 ReactCoCoaDemo[22051:2378052] 测试racTuple:<RACTwoTuple: 0x6000001982f0> (
    "\U5b57\U7b26\U4e321",
    "\U5b57\U7b26\U4e322"
)
    2021-07-03 14:46:26.456240+0800 ReactCoCoaDemo[22051:2378052] 测试RACTupleUnpack:字符串1-字符串2

@weakify、@strongify

  • RAC中使用@weakify、@strongify解决Block循环引用的问题。
  • 在block内部使用@strongify(self)后就可以使用self操作属性了,但是一定注意这两个宏定义一定要配合使用.可参考源码分析

@weakify(self);
//RAC处理手势,点击页面,隐藏键盘
[self.tapGesture.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
    @strongify(self);
    //经过宏定义处理后就可以使用self了,但此self非彼self。具体可查看源码分析
    [self.view endEditing:YES];
}];

信号时间操作

interval

创建定时器信号,每固定时间发送一次信号

   RACSignal *intervalSignal =  [RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]];
    //只知道使用take结束定时器这一种方法,不知道还有没有其他方法
    [[intervalSignal take:5] subscribeNext:^(NSDate * _Nullable x) {
        //订阅定时器信号,启动定时器,只打印5次
        NSLog(@"%@",x);
    }];

delay

延迟发送sendNext

 RACSignal *delaySignal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"杭杭"];
        return nil;
    }];
    
    //10秒后才收到消息,执行打印
    [[delaySignal delay:10] subscribeNext:^(id  _Nullable x) {
        NSLog(@"延迟:%@",x);
    }];

timeout

  1. 可以设置超时操作,让一个信号在规定时间之后自动报错
  2. 创建信号时不能使用sendCompleted,因为这样的话一旦发送了消息就取消订阅了。

 RACSignal *timeOutSignal = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"timeOutSignal发送信号"];
        //[subscriber sendCompleted];
        return nil;
    }] timeout:5 onScheduler:[RACScheduler currentScheduler]];
       
    [timeOutSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"timeOutSignal:%@",x);
     } error:^(NSError * _Nullable error) {
        //5秒后执行打印:
        //timeOutSignal:出现Error-Error Domain=RACSignalErrorDomain Code=1 "(null)"
        NSLog(@"timeOutSignal:出现Error-%@",error);
    } completed:^{
        NSLog(@"timeOutSignal:complete");
    }];

信号发送顺序 doNext、doCompleted

  • doNext:在订阅者发送消息sendNext之前执行
  • doCompleted:在订阅者发送完成sendCompleted之后执行
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"发送信号:1"];
        [subscriber sendCompleted];
        return nil;
    }];
    
    [[[signal doNext:^(id  _Nullable x) {
        NSLog(@"执行doNext");
    }] doCompleted:^{
        NSLog(@"执行doComplete");
    }] subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号:%@",x);
    }];

    2021-07-03 15:17:40.297460+0800 ReactCoCoaDemo[22624:2413314] 执行doNext
    2021-07-03 15:17:40.297631+0800 ReactCoCoaDemo[22624:2413314] 订阅信号:发送信号:1
    2021-07-03 15:17:40.297781+0800 ReactCoCoaDemo[22624:2413314] 执行doComplete

Retry

retry:只要失败就重新执行信号

    static int signalANum = 0;
    RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        if (signalANum == 3) {
            [subscriber sendNext:@"signalANum is 5"];
            [subscriber sendCompleted];
        }else{
            NSLog(@"signalANum错误!!!");
            [subscriber sendError:nil];
        }
        signalANum++;
        return nil;
    }];
      
    [[signalA retry] subscribeNext:^(id  _Nullable x) {
        NSLog(@"StringA-Next:%@",x);
    } error:^(NSError * _Nullable error) {
        //特别注意:这里并没有打印
         NSLog(@"signalA-Errror");
    }] ;


    2021-07-03 15:48:21.917009+0800 ReactCoCoaDemo[23117:2434958] signalANum错误!!!
    2021-07-03 15:48:21.965678+0800 ReactCoCoaDemo[23117:2434958] signalANum错误!!!
    2021-07-03 15:48:21.965896+0800 ReactCoCoaDemo[23117:2434958] signalANum错误!!!
    2021-07-03 15:48:21.966096+0800 ReactCoCoaDemo[23117:2434958] StringA-Next:signalANum is 3

RACMulticastConnection

当信号,被多次订阅时,为了避免多次调用创建信号中的block

    //普通多次订阅型号,多次重复请求
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        //发送信号
        NSLog(@"发送热门模块请求");
        [subscriber sendNext:@"1"];
        return nil;
    }];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅1 : %@", x);
    }];
    
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅2 : %@", x);
    }];
    2021-07-01 13:59:27.198749+0800 ReactCoCoaDemo[87616:2393341] 发送热门模块请求
    2021-07-01 13:59:27.198889+0800 ReactCoCoaDemo[87616:2393341] 订阅1 : 1
    2021-07-01 13:59:27.199095+0800 ReactCoCoaDemo[87616:2393341] 发送热门模块请求
    2021-07-01 13:59:27.199166+0800 ReactCoCoaDemo[87616:2393341] 订阅2 : 1


   // 1. 创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable* (id subscriber) {
    NSLog(@"发送热门模块请求");
    [subscriber sendNext:@1];
    return nil;
    }];
    
    // 2. 把信号转换成连接类
     RACMulticastConnection *signalConn = [signal publish];
    // 确定源信号的订阅者RACSubject
//    RACMulticastConnection *signalConn = [signal multicast:[RACReplaySubject subject]];
    // 3. 多个订阅
    [signalConn.signal subscribeNext:^(id _Nullable x) {
    NSLog(@"订阅1 : %@", x);
    }];
    [signalConn.signal subscribeNext:^(id _Nullable x) {
    NSLog(@"订阅2 : %@", x);
    }];
    [signalConn.signal subscribeNext:^(id _Nullable x) {
    NSLog(@"订阅3 : %@", x);
    }];
    // 4. 连接
    [signalConn connect];
    
    2021-07-01 13:38:48.718610+0800 ReactCoCoaDemo[87290:2376442] 发送热门模块请求
    2021-07-01 13:38:48.718764+0800 ReactCoCoaDemo[87290:2376442] 订阅1 : 1
    2021-07-01 13:38:48.718851+0800 ReactCoCoaDemo[87290:2376442] 订阅2 : 1
    2021-07-01 13:38:48.718930+0800 ReactCoCoaDemo[87290:2376442] 订阅3 : 1

RACCommond

RACCommond是用于处理事件的类,可以把事件如何处理,如何传递都封装到类中,之后就可以方便的调起它的执行方法。在实际开发中,我们可以用它来封装一个网络操作。

注意:

  1. 创建方法中block返回一个信号,且不能为nil,但是可以使用[RACSignal empty]表示空信号
  2. RACCommand必须被强引用,否则容易被释放

示例1

    RACCommand *commond = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        NSLog(@"input:%@",input);
        return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"执行完命令之后产生的数据"];
            return nil;
        }];
    }];
    
    //执行命令
    RACSignal *signal = [commond execute:@"输入的指令"];
    
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];

示例2

    //1.创建RACCommand:initWithSignalBlock
    self.command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
        //我们常在这里创建一个网络请求的信号,也就是封装一个请求数据的操作。
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"网络请求的信号"];
            //数据传递完成,必须调用sendComplleted.,否则永远处于执行中。
            [subscriber sendCompleted];
            return nil;
        }];
        return signal;
    }];
    
    //2.订阅RACCommand中的信号,要等到RACCommand执行后,才能收到消息
    [self.command.executionSignals subscribeNext:^(id  _Nullable x) {
        //这里是一个信号中信号
        [x subscribeNext:^(id  _Nullable x) {
            NSLog(@"收到信号:%@",x);
        }];
    }];
    
    //改进订阅方法:switchToLatest可以直接获取信号中信号
    [self.command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"改进-收到信号:%@",x);
    }];
    
    
    //3.监听RACCommand命令是否执行完毕的信号
    //默认会监测一次,所以可以使用skip表示跳过第一次信号。
    //这里可以用于App网络请求时,控制加载提示视图的隐藏或者显示
    [[self.command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {
        if([x boolValue] == YES){
            NSLog(@"RACCommand命令正在执行...");
        }else{
            NSLog(@"RACCommand命令不在执行中!!!");
        }
    }];
    
    //4.执行RACComand
    //方法:- (RACSignal *)execute:(id)input
    [self.command execute:@"start"];
    
    2021-07-03 14:37:18.213307+0800 ReactCoCoaDemo[21909:2369050] RACCommand命令正在执行...
    2021-07-03 14:37:18.213722+0800 ReactCoCoaDemo[21909:2369050] 收到信号:网络请求的信号
    2021-07-03 14:37:18.213857+0800 ReactCoCoaDemo[21909:2369050] 改进-收到信号:网络请求的信号
    2021-07-03 14:37:18.214260+0800 ReactCoCoaDemo[21909:2369050] RACCommand命令不在执行中!!!

bind

  //创建信号
    RACSubject *subject = [[RACSubject alloc] init];
    //绑定信号
    RACSignal *bindSignal = [subject bind:^RACSignalBindBlock {
        return ^ RACSignal *(id value,BOOL *stop){
            return [RACReturnSignal return:value];
        };
    }];
    //订阅信号
    [bindSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"接收到: %@",x);
    }];
    //发送信号
    [subject sendNext:@"原始数据"];

映射

flattenMap

 //创建信号
    RACSubject *subject = [[RACSubject alloc] init];
    RACSignal *signal = [subject flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
        value = [NSString stringWithFormat:@"处理数据:%@",value];
        //返回信号用来包装修改过的内容
        return [RACReturnSignal return:value];
    }];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    //发送信号
    [subject sendNext:@"22"];
    
    2021-07-01 16:15:29.503411+0800 ReactCoCoaDemo[89611:2497692] 处理数据:22

Map

    //创建信号
    RACSubject *subject = [[RACSubject alloc] init];
    RACSignal *signal = [subject map:^id _Nullable(id  _Nullable value) {
        //返回信号用来包装修改过的内容
        return [NSString stringWithFormat:@"处理数据:%@",value];
    }];
    
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];
    //发送信号
    [subject sendNext:@"123"];
    [subject sendNext:@"321"];

distinctUntilChanged

当上一次的值和当前的值有变化就会发出信号,否则会被忽略掉。 在开发中,刷新UI经常使用,只有两次数据不一样才需要刷新

   //创建信号
    RACSubject*signal = [RACSubject subject];
    
    //调用方法后订阅信号
    [[signal distinctUntilChanged] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    
    [signal sendNext:@"luobo"];
    [signal sendNext:@"luobo"];
    [signal sendNext:@"luobo"];
    [signal sendNext:@"luo"];
    
    2021-07-02 11:08:11.287061+0800 ReactCoCoaDemo[4515:3090401] luobo
    2021-07-02 11:08:11.287226+0800 ReactCoCoaDemo[4515:3090401] luo

filter

过滤信号,获取满足条件的信号

 //过滤信号,获取满足条件的信号
    [[self.userTF.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
            return value.length > 10;
    }] subscribeNext:^(NSString * _Nullable x) {
        self.pwdTF.backgroundColor = [UIColor blueColor];
    }];

ignore

忽略完某些值的信号

    //创建信号
    RACSubject*signal = [RACSubject subject];
    //ignore是忽略一些值
    RACSignal*ignoreSignal = [signal ignore:@"luobo"];
    //订阅信号
    [ignoreSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"%@",x);
    }];

    //发送信号
    [signal sendNext:@"luobo"];
    [signal sendNext:@"crazy"];
    
    2021-07-02 11:16:32.091836+0800 ReactCoCoaDemo[4645:3097841] crazy

信号取值

take、takeLast

    //创建信号
    RACSubject*subject = [RACSubject subject];
    // take是取前面的几个值
    //订阅信号
    [[subject take:2] subscribeNext:^(id x) {
        NSLog(@"###%@",x);
    }];

    // takeLast:去后面的多少个值,必须是发送完的
    //只有调用[subject sendCompleted];才会发送信号
    [[subject takeLast:2]subscribeNext:^(id x) {
        NSLog(@"***%@",x);
    }];

    //发送数据
    [subject sendNext:@"1"];
    [subject sendNext:@"2"];
    [subject sendNext:@"3"];
    [subject sendCompleted];
    [subject sendNext:@"4"];
    
    2021-07-02 14:35:42.284337+0800 ReactCoCoaDemo[7431:3227190] ###1
    2021-07-02 14:35:42.284516+0800 ReactCoCoaDemo[7431:3227190] ###2
    2021-07-02 14:35:42.284667+0800 ReactCoCoaDemo[7431:3227190] ***2
    2021-07-02 14:35:42.284724+0800 ReactCoCoaDemo[7431:3227190] ***3

takeUntil

    RACSubject *signalA = [RACSubject subject];
    [signalA subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号A:%@",x);
    }];
    __weak typeof(self)weakSelf = self;
    //[RACObserve(self, currentText)发送消息直到signalA信号结束
    [[RACObserve(self, currentText) takeUntil:signalA] subscribeNext:^(id  _Nullable x) {
        NSLog(@"打印结果:%@",x);
        weakSelf.label.text = x;
    }];

    self.currentText = @"0";
    self.currentText = @"1";
    self.currentText = @"2";
    [signalA sendNext:@"xxxx"];
    [signalA sendCompleted];//信号A结束之后,监听testLabel文本的信号也不在发送消息了
    self.currentText = @"3";

skip

    RACSubject*subject = [RACSubject subject];
    [[subject skip:2] subscribeNext:^(id x) {//跳跃过两个,执行下面的几个
        NSLog(@"%@", x);
    }];

    [subject sendNext:@"hang"];
    [subject sendNext:@"1"];
    [subject sendNext:@"3"];
    [subject sendNext:@"4"];

    2021-07-02 14:59:38.208491+0800 ReactCoCoaDemo[7833:3247502] 3
    2021-07-02 14:59:38.208576+0800 ReactCoCoaDemo[7833:3247502] 4

sequence

数组

        NSArray *tempArray = @[@"rose",@"jerry",@"kati"];
        [tempArray.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];

字典

     NSDictionary *dict = @{@"name":@"rose",@"age":@"18",@"height":@"1.67"};
    [dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
    //  RACTupleUnpack(NSString *key,NSString *value) = x; 解元祖注意一一对应
        RACTwoTuple *tuple = x;
        NSLog(@"%@:%@ \n",tuple[0],tuple[1]);
    }];

信号合并

combineLatest

    RACSubject *signalOne = [RACSubject subject];
    [signalOne subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号one:%@",x);
    }];

    RACSubject *signalTwo = [RACSubject subject];
    [signalTwo subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号Two:%@",x);
    }];
    
    //多个信号都至少有过一次订阅信号sendNext的操作,才会触发合并的信号
    [[RACSignal combineLatest:@[signalOne,signalTwo]] subscribeNext:^(RACTuple * _Nullable x) {
        //解元组:合并信号得到的是一个元组,里面存放的是两个信号发送的消息
        RACTupleUnpack(NSString *str1,NSString *str2) = x;
        NSLog(@"combineLatest:结果1:%@,结果2:%@",str1,str2);
    }];

    [signalOne sendNext:@"1"];
    [signalTwo sendNext:@"2"];

reduce

信号聚合,combineLatest合并后的信号订阅,得到的是一个元组,然而在开发中,我们往往需要检测多个信号合并后的效果(比如用户名和密码信号有效时,登录按钮才可以点击),这里就用到了reduce来实现信号聚合

    RACSubject *signalOne = [RACSubject subject];
    [signalOne subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号one:%@",x);
    }];

    RACSubject *signalTwo = [RACSubject subject];
    [signalTwo subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号Two:%@",x);
    }];
    
     //多个信号都至少有过一次订阅信号sendNext的操作,才会触发合并的信号
     [[RACSignal combineLatest:@[signalOne,signalTwo] reduce:^id(NSString *strOne,NSString *strTwo){
         return [NSString stringWithFormat:@"%@-%@",strOne,strTwo];
    }] subscribeNext:^(RACTuple * _Nullable x) {
        NSLog(@"combineLatest-reduce:%@",x);
    }];

    [signalOne sendNext:@"1"];
    [signalTwo sendNext:@"2"];
    
    2021-07-02 16:27:20.003904+0800 ReactCoCoaDemo[9024:3313688] 订阅信号one:1
    2021-07-02 16:27:20.004070+0800 ReactCoCoaDemo[9024:3313688] 订阅信号Two:2
    2021-07-02 16:27:20.004285+0800 ReactCoCoaDemo[9024:3313688] combineLatest-reduce:1-2

merge

    RACSubject *signalOne = [RACSubject subject];
    [signalOne subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号one:%@",x);
    }];

    RACSubject *signalTwo = [RACSubject subject];
    [signalTwo subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号Two:%@",x);
    }];
    
    RACSignal *mergeSignal = [signalOne merge:signalTwo];
    [mergeSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"合并信号:%@",x);
    }];
    //只调用其中一个信号,就会触发merge合并的信号
    [signalOne sendNext:@"1"];
    [signalTwo sendNext:@"2"];
    
    2021-07-02 16:46:30.205544+0800 ReactCoCoaDemo[9315:3329504] 订阅信号one:1
    2021-07-02 16:46:30.205685+0800 ReactCoCoaDemo[9315:3329504] 合并信号:1
    2021-07-02 16:46:30.205865+0800 ReactCoCoaDemo[9315:3329504] 订阅信号Two:2
    2021-07-02 16:46:30.205928+0800 ReactCoCoaDemo[9315:3329504] 合并信号:2

zipWith

  • zipWith把两个信号压缩成为一个信号,只有当两个信号同时发出信号时,两个信号的内容才会被合并为一个元组,触发压缩流的next事件。
  • 比如:当一个界面多个请求的时候,要等所有请求完成才更新UI。
  • 元组内元素顺序只与压缩信号的顺序有关,与发送信号的顺序无关
  RACSubject *signalOne = [RACSubject subject];
    [signalOne subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号one:%@",x);
    }];

    RACSubject *signalTwo = [RACSubject subject];
    [signalTwo subscribeNext:^(id  _Nullable x) {
        NSLog(@"订阅信号Two:%@",x);
    }];
    
    RACSignal *zipSignal = [signalOne zipWith:signalTwo];
    [zipSignal subscribeNext:^(id  _Nullable x) {
       //解元组:合并信号得到的是一个元组,里面存放的是两个信号发送的消息
       RACTupleUnpack(NSString *str1,NSString *str2) = x;
       NSLog(@"zipSignal:%@,%@",str1,str2);
    }];

    [signalOne sendNext:@"zipSignalOne"];
    [signalTwo sendNext:@"zipSignalTwo"];

    2021-07-02 16:55:43.208307+0800 ReactCoCoaDemo[9519:3338914] 订阅信号one:zipSignalOne
    2021-07-02 16:55:43.208468+0800 ReactCoCoaDemo[9519:3338914] 订阅信号Two:zipSignalTwo
    2021-07-02 16:55:43.208601+0800 ReactCoCoaDemo[9519:3338914] zipSignal:zipSignalOne,zipSignalTwo

综合案例

场景:比如用户名和密码信号有效时,登录按钮才可以点击

    RACSignal *userSignal = [self.userTF.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
        if (value.length >=5 && value.length <= 10) {
            return @(YES);
        }
        return @(NO);
    }];
    
    //combineLatest 信号合并,拿到各个信号的最新的值,每个合并的signal至少都有过一次sendNext,才会触发合并的信号
    //reduce 把信号发出元组的值聚合成一个值
    RACSignal *pwdSignal = [self.pwdTF.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
        if (value.length >=5 && value.length <= 10) {
            return @(YES);
        }
        return @(NO);
    }];
    
    //信号绑定宏
    RAC(self.loginBtn,userInteractionEnabled) = [RACSignal combineLatest:@[userSignal,pwdSignal] reduce:^id(NSNumber *userRes,NSNumber *pwdRes){
        return @(userRes.boolValue && pwdRes.boolValue);
    }];

信号拼接

contact

  • 使用concat可以按序拼接多个信号,拼接后的信号按序执行。
  • 使用concat连接信号后,每个信号无需再单独订阅,其内部会按序自动订阅
  • 前面的信号必须执行sendCompleted,后面的信号才会被激活
   RACSignal *signalOne = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"signalOne"];
        [subscriber sendCompleted];
        return nil;
    }];
        
    RACSignal *signalTwo = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"signalTwo"];
        [subscriber sendCompleted];
        return nil;
    }];

    RACSignal *signalThree = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        [subscriber sendNext:@"signalThree"];
        [subscriber sendCompleted];
        return nil;
    }];
       
    //拼接了三个信号,订阅之后,三个信号依次激活
    RACSignal *concatSignal = [[signalOne concat:signalThree] concat:signalTwo];
    [concatSignal subscribeNext:^(id  _Nullable x) {
        NSLog(@"信号被激活:%@",x);
    }];

    2021-07-02 17:08:13.103446+0800 ReactCoCoaDemo[9687:3348607] 信号被激活:signalOne
    2021-07-02 17:08:13.103610+0800 ReactCoCoaDemo[9687:3348607] 信号被激活:signalThree
    2021-07-02 17:08:13.103720+0800 ReactCoCoaDemo[9687:3348607] 信号被激活:signalTwo

then

  • 使用then连接信号,上一个信号完成后,才会连接then返回的信号,所以then连接的上一个信号必须使用sendCompleted,否则后续信号无法执行。
  • then连接的多个信号与concat不同的是:之前的信号会被忽略掉,即订阅信号只会接收到最后一个信号的值
    [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"信号1");
            [subscriber sendNext:@"发送信号1"];
            [subscriber sendCompleted];
            return nil;
        }] then:^RACSignal *{
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                NSLog(@"信号2");
                [subscriber sendNext:@"发送信号2"];
                [subscriber sendCompleted];
                return nil;
            }];
        }]then:^RACSignal * _Nonnull{
            return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
                NSLog(@"信号3");
                [subscriber sendNext:@"发送信号3"];
                [subscriber sendCompleted];
                return nil;
            }];
        }] subscribeNext:^(id x) {
            //只能接收到最后一个信号的值
            NSLog(@"订阅信号:%@",x);
    }];

    2021-07-02 17:18:02.363115+0800 ReactCoCoaDemo[9823:3356132] 信号1
    2021-07-02 17:18:02.363388+0800 ReactCoCoaDemo[9823:3356132] 信号2
    2021-07-02 17:18:02.363585+0800 ReactCoCoaDemo[9823:3356132] 信号3
    2021-07-02 17:18:02.363657+0800 ReactCoCoaDemo[9823:3356132] 订阅信号:发送信号3

www.wenjiangs.com/doc/bbr2cr0…