RACCommand 是一个在 ReactiveCocoa 中比较复杂的类,大多数使用 ReactiveCocoa 的人,尤其是初学者并不会经常使用它。
在很多情况下,虽然使用 RACSignal 和 RACSubject 就能解决绝大部分问题,但是 RACCommand 的使用会为我们带来巨大的便利,尤其是在与副作用相关的操作中。
RACCommand 简介
RACCommand 并不表示数据流,它只是一个继承自 NSObject的类,但是它却可以用来创建和订阅用于响应某些事件的信号。
它本身并不是一个 RACStream 或者 RACSignal 的子类,而是一个用于管理 RACSignal 的创建与订阅的类。
在 ReactiveCocoa 中的 FrameworkOverview 部分对 RACCommand 有这样的解释:
A command, represented by the RACCommand class, creates and subscribes to a signal in response to some action. This makes it easy to perform side-effecting work as the user interacts with the app.
在用于与 UIKit 组件进行交互或者执行包含副作用的操作时,RACCommand 能够帮助我们更快的处理并且响应任务,减少编码以及工程的复杂度。
RACCommand 的初始化与执行
在 -initWithSignalBlock: 方法的方法签名上,你可以看到在每次 RACCommand 初始化时都会传入一个类型为 RACSignal * (^)(InputType _Nullable input) 的 signalBlock:
-(instancetype)initWithSignalBlock:(RACSignal*(^)(InputType _Nullable input))signalBlock;
输入为 InputType 返回值为 RACSignal *,而 InputType 也就是在调用 -execute: 方法时传入的对象:
-(RACSignal*)execute:(nullable InputType)input
我们以下面的代码为例,先来看一下 RACCommand 是如何工作的:
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(NSNumber * _Nullable input) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
NSInteger integer = [input integerValue];
for (NSInteger i = 0; i < integer; i++) {
[subscriber sendNext:@(i)];
}
[subscriber sendCompleted];
return nil;
}];
}];
[[command.executionSignals switchToLatest] subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
[command execute:@1];
[RACScheduler.mainThreadScheduler afterDelay:0.1
schedule:^{
[command execute:@2];
}];
[RACScheduler.mainThreadScheduler afterDelay:0.2
schedule:^{
[command execute:@3];
}];
首先使用 -initWithSignalBlock: 方法创建一个 RACCommand 的对象,传入一个类型为 InputType -> RACSignal
RACCommand 的使用
RACCommand 非常适合封装网络请求,我们可以使用下面的代码封装一个网络请求:
-(instancetype)init{
if (self = [super init]) {
RACSignal *usernameSingal = [RACObserve(self, name) map:^id _Nullable(id _Nullable value) {
if ([(NSString *)value length] >6) {
return @(YES);
}
return @(NO);
}];
RACSignal *passwordSingl = [RACObserve(self, password) map:^id _Nullable(id _Nullable value) {
if ([(NSString *)value length] > 6) return @(YES);
return @(NO);
}];
RACSignal *loginSingl = [RACSignal combineLatest:@[passwordSingl,usernameSingal] reduce:^id(NSNumber *username,NSNumber *passwordSingl){
return @([username boolValue] && [passwordSingl boolValue]);
} ];
_loginButtonCommand = [[RACCommand alloc]initWithEnabled:loginSingl signalBlock:^RACSignal * _Nonnull(id _Nullable input) {
return [self loginWithUserName:self.name password:self.password];
}] ;
}
return self;
}
-(RACSignal *)loginWithUserName:(NSString *)name password:(NSString *)passWord{
if (name.length > 6 && passWord.length > 6) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
[subscriber sendNext:[NSString stringWithFormat:@"姓名:%@ 密码:%@",self.password,self.name]];
[subscriber sendCompleted];
return nil;
}];
}
return nil;
}
button.rac_command = self.modelViews.loginButtonCommand;
RAC(self.modelViews,name) = textfield.rac_textSignal;
RAC(self.modelViews,password) = textfield1.rac_textSignal;
[[self.modelViews.loginButtonCommand executionSignals] subscribeNext:^(id _Nullable x) {
[x subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
}];