ReactiveObjc初探

2018-06-07  本文已影响4人  xuzhenhao

一. 存在的问题

  1. UI更新必须依赖程序员在指定位置手动触发。UI更新的逻辑会遍布各地难以维护,而且调用者必须明确的知道更新的逻辑,因为updatA,updateB,先调后调有时也会发生截然不同的效果。

  2. 举个🌰,假如有一个类似网易云音乐的列表页。歌曲列表请求成功后,赋值tabelView的数据源。注意,此时我们还有执行[tableView reloadData],此外,可能还需要调更新歌曲数,更新专辑封面等等。只要和这个数据源有关的UI元素,我们都需要手动触发一遍。更重要的是要更新哪些UI,需要我们自己判断,极容易引发bug。上述是网络请求的,在类似的多交互页面中,常常还包括侧滑删除、批量增加、名称修改等等对数据源的操作,而这些都意味着我们需要手动触发UI更新。当然,有些共同的更新,我们可以抽成一个方法,但还是难以从根本上避免伴随着用户事件及数据改变的,散落各地的UI更新逻辑。

二. RAC的解决方案

  1. 还是上面那个场景,使用RAC后,只需要关心数据源如果变化,无需关心数据源变化后所引发的UI层面的连锁反应。加载更多,往数据源中加对应数据;删除某首歌,删除数据源中的对应数据。修改数据后,订阅(subscribe)了数据源的相关元素会自动接收到数据变化的信号,并作出对应改变。

  2. 光是上述效果我们用KVO也能做到,RAC更独特的地方在于用信号量(Signal)统一了所有变化,包括网络回调、KVO、通知、block等等,抽象成信号量后,我们可以统一进行逻辑操作,包括combine、filter、map等等,这些操作才是强力的工具。整个流程为事件流->信号流->逻辑操作->订阅。

三. 适合使用RAC的场景举例

  1. 登录注册:登录注册常见的需求有必须同时满足xxx条件,按钮才能启用。

@weakify(self);

    //自动响应登录按钮是否可用

    RAC(self.loginButton, enabled) = [RACSignal

                          combineLatest:@[self.accountTextField.rac_textSignal,

                                          self.pwdTextField.rac_textSignal]

                           reduce:^(NSString *userName,NSString *pwd){

                            return @(userName.length > 0 && pwd.length > 0);

                             }];

    //account属性自动响应更新值

    [[self.accountTextField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {

        return value.length > 0;

    }] subscribeNext:^(NSString * _Nullable x) {

        @strongify(self);

        self.loginViewModel.account = x;

    }];

    //password属性自动响应更新值

    [[self.pwdTextField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {

        return value.length > 0;

    }] subscribeNext:^(NSString * _Nullable x) {

        @strongify(self);

        self.loginViewModel.password = x;

    }];

    //按钮点击事件转成信号流,调用VM中的方法执行。

    [[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {

        @strongify(self);

        [[self.loginViewModel.loginCommand executing] subscribeNext:^(id  _Nullable x) {

            NSLog(@"登录成功");

        }];

        [[self.loginViewModel.loginCommand execute:nil] subscribeError:^(NSError * _Nullable error) {

            NSLog(@"登录失败");

        }];

    }];



vm.m

- (void)setupCommand{ 
          @weakify(self); 
        _loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *                        _Nonnull(id _Nullable input) {
         return [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {

            @strongify(self);

            [self loginWithUserName:self.account password:self.password done:^(NSDictionary *result) {

                NSString *userId = [result objectForKey:@"user_id"];

                if (userId) {
                    [subscriber sendCompleted];
                }else{
                    [subscriber sendError:nil];
                }
            }];
            return nil;
        }];
    }];
}
上一篇下一篇

猜你喜欢

热点阅读