工作生活

RAC 初探

2019-07-08  本文已影响0人  J扣歪

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;
}
上一篇下一篇

猜你喜欢

热点阅读