RACCommand 一般我们都是放在ViewModel中,就像是一个Action,由用户或者View/ViewController的。
而且很多时候,我们不仅仅需要让这个action执行起来,而且还需要知道执行的结果。这里简单写个demo。
@interface ViewModel
- (RACCommand*)testCommand; @end @implement ViewModel
- (RACCommand*)testCommand{ return [RACCommand alloc] initWithSignalBlock:^RACSignal *(id input){ return [RACSignal createSignal:^RACDisposable *(id subscriber) { [subscribe sendError:[NSError errorWithDomain:@"" code:0 userInfo:nil]]; }]; //1 }]; @end
如果ViewController中是这么写的。
self.testBtn.rac_command = self.viewModel.testCommand;
现在有这样一个需要,就是知道这个command什么时候被执行了。执行的结果是咋样的,是否有错误,或者完成,那么应该怎么办呢。
之前我是这么写的。
[[self.testBtn.rac_command executing] subscribeNext:^(NSNumber *executing) {
if([executing boolValue]){
NSLog(@"正在执行"); //2
}
}];
[self.testBtn.rac_command.errors subscribeError:^(NSError *error)]{
NSLog(@"发生%@",error); //3
}];
[self.testBtn.rac_command.executionSignals flattenMap:^(RACSignal *exection){
return [[exection ignoreAll] concate:[RACSignal return:RACUnit.defaultUnit]]; //4
} subscribeNext:^(_){
NSLog(@"执行完毕"); //5
}];
感觉没有问题,但是实际的问题来。就是在viewModel中sendError得话,那么3的位置是能够执行到得,但是我不期望的5也被执行了。跟踪之后,发现原来RACCommand里面本身会对error进行catch的然后concate 返回一个error的signal了。所以4中本来应该发现error直接出去的都变成了普通的return signal了。
google一下吧,看看有什么好的解决办法。发现使用materialize 和dematerialize的规避吧。materialize回家所有的信号封装成event的形势出去。这样RACCommand就拿不到error了。也就是说2处也不会有了。只有在需要的地方也就是我们的ViewController中使用dematerialize出来拿到我们实际想要的数据了。
改动不大。在ViewModel中1处成
return [xxxxx materialize];
然后在ViewController中,将3部分代码可以去掉了。executionSignals 的部分需要改成
[self.testBtn.rac_command.executionSignals subscribeNext:^(RACSignal *execution) {
[[[execution dematerialize] deliverOn:[RACScheduler mainThreadScheduler]] subscribeError:^(NSError *error) {
NSLog(@"发生错误");
} completed:^{
NSLog(@"完成");
}];
}];
这样,所有你想要的东西都可以得到了。