RAC 初探

作者: J扣歪 | 来源:发表于2019-07-08 15:29 被阅读0次

    RAC是利用函数式+响应式编程思想封装的一套框架.为低耦合高内聚开发提供了方便.

    一 基本用法

    1. UIButton
    [[_button rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
            NSLog(@"点击事件会触发这里");
        }];
    
    1. UITextField
     [_textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"实时监听texfield输入框的文字%@",x);
        }];
    

    3.KVO

    Person *p = Person.new;
        [p rac_observeKeyPath:@"name" options:(NSKeyValueObservingOptionNew) observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
            NSLog(@"KVO监听事件会触发这里");
        }];
        p.name = @"lala";
    
    1. 通知
     [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
            NSLog(@"键盘弹起会触发这里%@",x);
     }];
    
    Person *p = Person.new;
    //信号绑定 对象p的name属性会随_textField输入框的值实时变化
    RAC(p,name) = _textField.rac_textSignal;
    //KVO 属性监听p的name属性
    [RACObserve(p, name) subscribeNext:^(id  _Nullable x) {
           NSLog(@"监听到了%@",x);
     }];
    

    二 信号

    1. subject
      //创建信号
        RACSubject *subject = [RACSubject subject];
        //订阅信号
        [subject subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        //发送消息
        [subject sendNext:@"hello"];
    
    1. RACSignal
    //创建
    RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //发送
                [subscriber sendNext:@"模拟数据请求!"];
            });
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"信号销毁");
            }];
        }];
    //订阅
        [signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
    
    1. map
     RACSubject *subject = [RACSubject subject];
        [[subject map:^id _Nullable(id  _Nullable value) {
            NSLog(@"1==%@",value);
            return [NSString stringWithFormat:@"%@ RAC!",value];
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"2====%@",x);
        }];
        [subject sendNext:@"hello"];
    

    控制台输出:
    1==hello
    2====hello RAC!

    1. flattenMap
    RACSubject *subject = [RACSubject subject];
        [[[subject flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
            RACSignal *sign = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                    [subscriber sendNext:@"只有当我发送消息才会执行下一步"];
                });
                return nil;
            }];
            [sign subscribeNext:^(id  _Nullable x) {
                NSLog(@"1.%@",x);
            }];
            return sign;
        }] flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
            NSLog(@"2.%@",value);
            return [RACReturnSignal return:[NSString stringWithFormat:@"%@ 执行完毕!",value]];
        }]  subscribeNext:^(id  _Nullable x) {
             NSLog(@"3.%@",x);
        }];
        [subject sendNext:@"执行"];
    

    控制台输出:
    1.只有当我发送消息才会执行下一步
    2.只有当我发送消息才会执行下一步
    3.只有当我发送消息才会执行下一步 执行完毕!

    1. 同时处理多个信号
    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"模拟数据请求1"];
            return nil;
        }];
        RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"模拟数据请求2"];
            return nil;
        }];
        [self rac_liftSelector:@selector(recive:td:) withSignals:signal1,signal2, nil];
    - (void)recive:(id)data1 td:(id)data2{
        //只有当两个信号都发送了才会执行此方法
        NSLog(@"%@,%@",data1,data2);
    }
    

    控制台输出: 模拟数据请求1,模拟数据请求2

    和上面类似

    RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"模拟数据请求1"];
            return nil;
        }];
        RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"模拟数据请求2"];
            return nil;
        }];
        [[RACSignal combineLatest:@[signal1,signal2] reduce:^(NSString *d1,NSString *d2){
            NSLog(@"1.%@%@",d1,d2);
            return @"hello";
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"2.%@",x);
        }];
    

    控制台输出 :
    1.模拟数据请求1模拟数据请求2
    2.hello

    1. RACCommand
    RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            NSLog(@"1.%@",input);
            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                [subscriber sendNext:@"发送"];
                return nil;
            }];
        }];
        [command.executing subscribeNext:^(NSNumber * _Nullable x) {
            if ([x integerValue] == 0) {
                NSLog(@"正在执行");
            }else{
                NSLog(@"执行完毕");
            }
        }];
        RACSignal *commandSignal =  [command execute:@"执行"];
        [commandSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"2.%@",x);
        }];
    

    输出:
    正在执行
    1.执行
    执行完毕
    2.发送

     UIButton *btn = _button;
      btn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            //点击按钮回调这里
            NSLog(@"我是button:%@",input);
            return [RACSignal empty];
        }];
    

    三 RAC+MVVM 案例

    1.模拟登录请求

     //RAC绑定登录按钮是否可点击属性信号
        RAC(_loginButton,enabled) = [RACSignal combineLatest:@[_phoneField.rac_textSignal,
                            _passwordField.rac_textSignal]
        reduce:^(NSString *phoneText,NSString *passwordText){
            //只有当手机号和密码长度都大于0时返回yes
            return @(phoneText.length>0&&passwordText.length>0);
        }];
        
        //登录按钮点击事件
        @weakify(self);
        [[_loginButton rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIButton * loginBtn) {
            @strongify(self);
            [loginBtn setTitle:@"登录中..." forState:(UIControlStateNormal)];
    
            //返回订阅信号
            RACSignal *signal = [LoginViewModel  loginWith:self.phoneField.text password:self.passwordField.text];
    
            //订阅信号 等待登录请求返回
            [signal subscribeNext:^(id  _Nullable x) {
                NSLog(@"登陆成功");
                [loginBtn setTitle:@"登录" forState:(UIControlStateNormal)];
            }];
            [signal subscribeError:^(NSError * _Nullable error) {
                NSLog(@"登陆失败");
                [loginBtn setTitle:@"登录" forState:(UIControlStateNormal)];
            }];
        }];
    

    LoginViewModel 代码

    + (RACSignal *)loginWith:(NSString *)phonestr password:(NSString *)pwdstr{
        //创建信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            //模拟登录请求耗时
            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                //发送消息
                [subscriber sendNext:@"登录成功!"];
            });
            return nil;
        }];
        return signal;
    }
    

    2. 模拟数据加载

    调用代码

    //viewModel
        _viewModel = DataViewModel.new;
        //数据条数 用于模拟数据
        _data = 10;
        @weakify(self);
        //下拉刷新控件及事件
        _mainTableView.qy_header = [QYRefreshNormalHeader headerWithRefreshingBlock:^{
            @strongify(self);
            //发起下拉刷新数据请求 10条数据
            [self.viewModel.command execute:@(10)];
        }];
        //上拉加载控件及事件
        _mainTableView.qy_footer = [QYRefreshNormalFooter footerWithRefreshingBlock:^{
            @strongify(self);
            //发起上拉加载数据请求 +10
            [self.viewModel.command execute:@(self.data+10)];
        }];
        //订阅信号 等待数据返回 再刷新TableView
        [self.viewModel.command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
            @strongify(self);
            self.data = [x integerValue];
            [self.mainTableView reloadData];
            [self.mainTableView.qy_footer endRefreshing];
            [self.mainTableView.qy_header endRefreshing];
        }];
    

    DataViewModel 代码

    - (RACCommand *)command{
        if (!_command) {
            _command = [[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
                //返回信号
                return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                    
                    //模拟网络耗时请求
                    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
                        [subscriber sendNext:input];
                        [subscriber sendCompleted];//不加此句代码指令执行一次后将会被禁用
                    });
                    
                    return nil;
                }];
            }];
        }
        return _command;
    }
    

    相关文章

      网友评论

        本文标题:RAC 初探

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