信号
创建一个信号,每次订阅它可以执行它的block,并且在订阅的block中拿到它的sendxxx.
- 用于经常执行一个事件。 例如:计数器
- 将逻辑代码封装在信号中,在订阅block中触发信号事件并做收尾代码。
- 当创建信号中的block中的subscriber(订阅者)执行到sendCompeleted或者sendError则销毁订阅(该订阅block不会再因为subscribe的sendNext触发)
- 信号对象每次调用subscribeNext时会执行创建信号时subscriber所发送的所有sendNext直到遇到sendCompeleted或者sendError结束订阅触发disposableBlock
示例:
__block int i = 0;
RACSingal *singal = [RACSingal createSingal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//doSomthing...
[subscriber sendNext:@(++i)];
[subscriber sendCompeleted];
[subscriber sendError:$error];
retrun [RACDisposable disposableWithBlock:^{
//doSomthing...
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%li",[x integerValue]); // 1;
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%li",[x integerValue]); // 2;
}];
[signal subscribeCompleted:^{
NSLog(@"completed"); // completed;
}];
[signal subscribeError:^(NSError * _Nullable error) {
NSLog(@"%@",error); //$error
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%li",[x integerValue]); //5;
}];
所有类总结
- 主要就是用信号来触发事件 RACSingnal是基类
- RACSubject 是先订阅后发送信号 后执行所有订阅的block
- RACReplaySubject 是先发送信号 后每订阅一次则触发所有信号执行订阅block
- RACCommand UI控件会有一个rac_commend类别属性 传入一个RACCommand即可监听控件事件
- RAC($target,$prop)=xxxSingnal 可以给某个类的某个属性绑定一个信号.当信号sendNext:xxx(发送信号)时该属性会随之更改。
例:
//当文本框修改时会触发信号subscribe的sendNext:输入的内心,此时就能被obj的prop订阅的block接收到 并进行修改prop的值
RAC(obj,prop) = textField.rac_textSignal;
//用定时器修改UILabel
_subject = [RACSubject subject];
RAC(label,text) = _subject;
__block int i = 0;
@weakify(self)
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
@strongify(self)
if (++i>20) {
[timer invalidate];
}
[self.subject sendNext:[NSString stringWithFormat:@"%i",i]];
}];
UITextField有一个rac_textSignal属性可以监听它触发事件,
UIButton拿到事件信号[[UIButton buttonWithType:UIButtonTypeRoundedRect] rac_signalForControlEvents:UIControlEvents]
*RACCommand
- 该类是信号的封装
- 创建时调用一个带有传入参数返回信号的block
// 创建指令
RACCommand *commend = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable x) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
YPLog(@"%@",x);
[subscriber sendNext:@"next"];
return nil;
}];
}];
// 执行指令获取信号 并订阅
[[commend execute:@"execute"] subscribeNext:^(id _Nullable x) {
YPLog(@"%@",x);
} ];
// 指令只能执行一次 重复执行无效
[commend execute:@"execute2"];
*Filter(过滤信号值) Map(转换信号值) doNext(预处理)
- filter [$racSingal filter:^BOOL(id _Nullable x){}]filter返回一个BOOL值,用filter过滤信号发出的(sendNext)值,当满足filter方法时才会执行后面订阅的block.
- map [$racSingal filter:^id(id _Nullable x){}]map返回一个新的对象,将之前接收到的值进行处理返回一个新的值.然后执行后面订阅的block
- doNext doNext可以像subscribeNext一样获取到sendNext值但是它会返回信号给后面的订阅方法。
实例:
_subject = [RACSubject subject];
// 用filter map进行转换信号传递的值
// 每次filter map返回的都是一个新的信号
RACSignal *signal = [[_subject filter:^BOOL(id _Nullable value) {
return [value integerValue] >5;
}] map:^id _Nullable(id _Nullable value) {
return [NSString stringWithFormat:@"第%li个",[value integerValue]];
}];
RAC(label,text) = signal;
__block int i = 0;
@weakify(self)
[NSTimer scheduledTimerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
@strongify(self)
i++;
if (i>20) {
[timer invalidate];
}
[self.subject sendNext:[NSString stringWithFormat:@"%i",i]];
}];
*flattenMap then 返回一个新的信号
- 给信号调用flattenMap从block中返回一个新的信号来替换原来的信号。
- then和flattenMap的作用一样 不过then需要等待上一个信号执行了sendCompelete才会返回一个新的信号给下一个,一些UI控件是不会执行sendCompelete的.
- 因为subscribe一旦执行sendComplete就会销毁当前订阅,UI控件则不发送sendCompelete,让该信号的订阅block可以一直触发
- UI控件的信号使用flattenMap来替换信号
- 异步操作(网络请求等)使用then来替换信号
注意:
- 和上面的map区别 map只是对信号发送的值进行转换 flattenMap是替换该信号
- 订阅信号的error时 如果信号某个阶段执行sendError则直接进入error的block
*CombineLatest聚合
- combineLatest 聚合函数可以使多个不同的信号合并成一个新的信号,当所有信号的subscribe都发送出sendNext的时候就会调用合并信号后订阅的block
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"hello"];
return nil;
}];
RACSubject *subject = [RACSubject subject];
RACSignal *combineSignal = [RACSignal combineLatest:@[signal,subject] reduce:^id _Nullable(NSString*str1,NSString*str2){
YPLog(@"%@, %@",str1,str2);
return [str1 stringByAppendingString:str2];
}];
[combineSignal subscribeNext:^(id _Nullable x) {
YPLog(@"%@",x);
}];
//当该信号触发才会触发合并的信号
[subject sendNext:@"vijay"];
- 注意:一个信号添加了监听后除非手动调用信号的dispose(清空信号的监听),则监听会一直保留在信号上,重复监听则会重复叠加 千万不要重复叠加监听
- 可以同时监听sendNext sendError sendCompelete subscribeNext:^(id _Nullable x)nextBlock error:^(NSError * _Nullable error)errorBlockcompleted:^(void)completedBlock:
登录示例:
- (void)loginServiceWith:(NSString*)username :(NSString*)password compelete:(void(^)(BOOL result))callback{
callback(username.length&&password.length);
}
- (RACSignal*)loginSignal{
@weakify(self)
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
@strongify(self)
[self loginServiceWith:self.userNameField.text :self.passWordField.text compelete:^(BOOL result) {
[subscriber sendNext:@(result)];
[subscriber sendCompleted];
}];
return nil;
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
[self createUI];
[[[[self.loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] doNext:^(__kindof UIControl * _Nullable x) {
self.loginBtn.enabled = NO;
self.failText.hidden = YES;
}] flattenMap:^__kindof RACSignal * _Nullable(__kindof UIControl * _Nullable value) {
return [self loginSignal];
}] subscribeNext: ^(id _Nullable x) {
self.loginBtn.enabled = YES;
BOOL success = [x boolValue];
self.failText.hidden = success;
if (success) {
[SVProgressHUD showSuccessWithStatus:@"登录成功"];
}
}completed:^{
YPLog(@"compelete");
}];
}
替代代理
//给当前控制器对象的代理方法转换成信号并监听
[[self rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:) fromProtocol:@protocol(UITableViewDelegate) ] subscribeNext:^(RACTuple * x) {
// RACTuple是一个类数组(元组)里面保存了代理方法的所有参数
}];
//当tableview触发代理方法时就会激活该信号 并且执行订阅的block
table.delegate = self;