一. 存在的问题
-
UI更新必须依赖程序员在指定位置手动触发。UI更新的逻辑会遍布各地难以维护,而且调用者必须明确的知道更新的逻辑,因为updatA,updateB,先调后调有时也会发生截然不同的效果。
-
举个🌰,假如有一个类似网易云音乐的列表页。歌曲列表请求成功后,赋值tabelView的数据源。注意,此时我们还有执行[tableView reloadData],此外,可能还需要调更新歌曲数,更新专辑封面等等。只要和这个数据源有关的UI元素,我们都需要手动触发一遍。更重要的是要更新哪些UI,需要我们自己判断,极容易引发bug。上述是网络请求的,在类似的多交互页面中,常常还包括侧滑删除、批量增加、名称修改等等对数据源的操作,而这些都意味着我们需要手动触发UI更新。当然,有些共同的更新,我们可以抽成一个方法,但还是难以从根本上避免伴随着用户事件及数据改变的,散落各地的UI更新逻辑。
二. RAC的解决方案
-
还是上面那个场景,使用RAC后,只需要关心数据源如果变化,无需关心数据源变化后所引发的UI层面的连锁反应。加载更多,往数据源中加对应数据;删除某首歌,删除数据源中的对应数据。修改数据后,订阅(subscribe)了数据源的相关元素会自动接收到数据变化的信号,并作出对应改变。
-
光是上述效果我们用KVO也能做到,RAC更独特的地方在于用信号量(Signal)统一了所有变化,包括网络回调、KVO、通知、block等等,抽象成信号量后,我们可以统一进行逻辑操作,包括combine、filter、map等等,这些操作才是强力的工具。整个流程为事件流->信号流->逻辑操作->订阅。
三. 适合使用RAC的场景举例
- 登录注册:登录注册常见的需求有必须同时满足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;
}];
}];
}
网友评论