新项目今天提测,项目中用到了RAC&MVVM框架,简单记录下RAC的简单使用
项目是OC开发,用的是ReactiveObjC库
RAC — ReactiveCocoa(RAC) Github 上的一个开源函数响应编程框架!!
RAC里面最常见和常用的类
RACSignal 信号类
1.通过RACSignal创建一个信号 (默认是一个冷信号);
2.通过订阅者,订阅这个信号 (变成热信号);
3.发送信号
RACDisposable:它可以帮助我们取消订阅.
RACSubscriber(协议):订阅者(发送信号!)
RACSubject :信号提供者,自己可以充当信号,又能够发送信号!
RACReplaySubject: 可以先发送信号,再订阅,也可以先订阅再发送
RAC常用场景
可以代替代理
[[view rac_signalForSelector:@selector(btnclick:)] subscribeNext:^(RACTuple* _Nullablex) {
}];
可以代替KVO
[[view rac_valuesForKeyPath:@"frame"observer:self] subscribeNext:^(id _Nullablex) {
//x 是监听的属性改变的结果
NSLog(@"%@",x);
}];
监听事件
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindofUIControl* _Nullablex) {
NSLog(@"点击了");
}];
代替通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"%@",x);
}];
ps:虽然使用RAC代替通知可以免去手动移除通知的方法,但是又多了一步移除信号的操作.个人建议,用通知的话,还是用系统方法吧
监听文本框
[_tf.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
NSLog(@"%@",x);
}];
用于监听 UITextField 和 UITextView 文本的改变,比写代理什么的,方便多了
定时器
self.disposable = [[RACSignal interval:1.f onScheduler:[RACScheduler scheduler]]subscribeNext:^(NSDate * _Nullable x) {
//x是当前系统时间
NSLog(@"%@",x);
/* 关闭计时器 */
[_disposable dispose];
}];
ps:这个需要完成后关闭计时器
rac_liftSelector 适用于处理多个请求,都返回结果之后,刷新UI
RACSignal * signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
NSLog(@"1111");
[subscriber sendNext:@"数据1"];
return nil;
}];
RACSignal * signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
NSLog(@"2222");
[subscriber sendNext:@"数据2"];
return nil;
}];
[self rac_liftSelector:@selector(setupData::) withSignalsFromArray:@[signal1,signal2]];
使用注意事项:
几个信号,selector的方法就几个参数,每个参数对应信号发出的数据。
不需要主动订阅,内部会主动订阅
RACMulticastConnection 信号连接类,适用于一个信号有多个订阅者
RACSignal * signal = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
[subscriber sendNext:@"请求到的数据"];
returnnil;
}];
RACMulticastConnection* connection = [signal publish];
[connection.signal subscribeNext:^(id _Nullablex) {
NSLog(@"订阅的信号%@-----1",x);
}];
[connection.signal subscribeNext:^(id _Nullablex) {
NSLog(@"订阅的信号%@-----2",x);
}];
[connection connect];
RACCommand 创建命令
RACCommand* command = [[RACCommandalloc]initWithSignalBlock:^RACSignal*_Nonnull(id _Nullableinput) {
//input:指令 就是下边execute: 传的参数
return [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
[subscriber sendNext:@"发送的数据"];
//发送完成 写上命令才会监听到执行完毕
[subscribersend Completed];
returnnil;
}];
}];
//监听有没有执行完毕
[command.executing subscribeNext:^(NSNumber*_Nullablex) {
if([x boolValue]){//正在执行!!
NSLog(@"正在执行!!");
}else{
NSLog(@"已经结束或还没开始做");
}
}];
[command.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
NSLog(@"订阅-----%@",x);//这里就是上边发送的数据
}];
RAC常用的宏
给某个对象的某个属性绑定信号,一旦信号产生数据,就会将内容赋值给属性!
RAC(_label,text) = _textF.rac_textSignal;
只要这个对象的某个属性发生变化 类似KVO
[RACObserve(self.label,text)subscribeNext:^(id _Nullablex) {
NSLog(@"%@",x);
}];
包装元祖
RACTuple* tuple =RACTuplePack(@"1",@"2");
解包
RACTupleUnpack(NSString * str) = tuple;
解决循环引用问题 需要配套使用
@weakify(self);
@strongify(self);
RAC过滤
filter 过滤条件 筛选不通过的值
[[_textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
//value:源信号的内容
NSLog(@"value----%@",value);
return [value length] >5;
//返回值:就是过滤条件,只有满足这个条件,才能获取到内容
}]subscribeNext:^(NSString*_Nullablex) {
NSLog(@"x------%@",x);
}];
ignore 忽略一些值!! 一次只能忽略一个对象
RACSubject * subject = [RACSubject subject];
[[[subject ignore:@"ahaha"] ignore:@"1234"]subscribeNext:^(id _Nullablex) {
NSLog(@"%@",x);
}];
take\takeLast 拿出前边几条数据或者后边几条数据
RACSubject* subject = [RACSubject subject];
[[subject takeLast:3]subscribeNext:^(id _Nullablex) {
NSLog(@"%@",x);
}];
take:指定拿前面的哪几条数据!!(从前往后) 根据发送数据的前几条
takeLast: 取出发送的数据中 后面几条数据 ps: 必须调发送结束的方法后才会有数据返回
takeUntil 标记信号 取出数据 直到标记的信号发送数据截止
RACSubject * subject = [RACSubject subject];
//标记信号
RACSubject* tagSubject = [RACSubjectsubject];
[[subjecttakeUntil:tagSubject]subscribeNext:^(id _Nullablex) {
NSLog(@"x------%@",x);
}];
distinctUntilChanged 忽略掉重复数据
RACSubject * subject = [RACSubject subject];
[[subjectdistinctUntilChanged] subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
skip 按照发送数据的顺序略过几个值
RACSubject * subject = [RACSubject subject];
[[subjectskip:2]subscribeNext:^(id _Nullablex) {
NSLog(@"%@",x);
}];
RAC遍历字典或者数组
遍历数组
[arr.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
遍历字典
[dic.rac_sequence.signal subscribeNext:^(id _Nullable x) {
//这里x是元组,每个元组中包含字典的一对键值对 x[0]为key x[1]为value
RACTupleUnpack(NSString * key,NSString * value) = x;
NSLog(@"%@---%@",key,value);
}];
组合信号
concat 按顺序组合
RACSubject* subject1 = [RACSubject subject];
RACSubject* subject2 = [RACSubject subject];
RACSubject* subject3 = [RACSubject subject];
//订阅信号
[[RACSignal concat:@[subject1,subject2,subject3]]subscribeNext:^(id _Nullablex) {
NSLog(@"%@",x);
}];
ps: subject1 发送结束才会接受 subject2,依次类推
merge 组合信号 根据发送的情况接收数据,与数组中的顺序没有关系,哪个先发送就先收到哪个
RACSubject* subject1 = [RACSubject subject];
RACSubject* subject2 = [RACSubject subject];
RACSubject* subject3 = [RACSubject subject];
RACSignal* signal = [RACSignal merge:@[subject2,subject3,subject1]];
[signalsubscribeNext:^(id _Nullablex) {
//任意一个信号发送内容就会来这个Block
NSLog(@"%@",x);
}];
zipWith : 两个信号压缩!只有当两个信号同时发出信号内容,并且将内容合并成为一个元组
RACSubject* subject1 = [RACSubject subject];
RACSubject* subject2 = [RACSubject subject];
[[subject1 zipWith:subject2]subscribeNext:^(id _Nullablex) {
//只有两个信号都发送数据 才会来到这里 少一个不行
//比如,subject1 发送了一个字符串@"1",又发送了一个字符串@"2",这时subject2才发送一个字符串@"a",那么这里过来的是@"2"和@"a"组成的元组
NSLog(@"%@",x);
}];
RAC个人感觉配合MVVM使用更方便些
比如,在 viewModel 中请求数据/处理数据, VC中订阅信号即可
viewModel 中
@weakify(self);
_parseCommand= [[RACCommand alloc] initWithSignalBlock:^RACSignal*_Nonnull(id _Nullableinput) {
return [RACSignal createSignal:^RACDisposable*_Nullable(id _Nonnullsubscriber) {
@strongify(self);
[Network postRequestWithApi:api param:nil result:^(id data) {
[subscriber sendNext:self.dataArr];
[subscriber sendCompleted];
}];
return nil;
}];
}];
}
VC中
@weakify(self);
[self.viewModel.parseCommand.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
@strongify(self);
}];
[self.viewModel.parseCommand execute:param];
或者
viewModel中
- (RACSignal*)signalForSearchOrgWithName:(NSString*)orgName
{
@weakify(self);
RACSignal * signal = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {
@strongify(self);
[subscriber sendNext:self.orgDataArr];
[subscriber sendCompleted];
return nil;
}];
return signal;
}
然后在VC中调用订阅
在tableview的cell使用,需要注意复用的问题
@weakify(self);
[[[photoCell.photographBtn rac_signalForControlEvents:UIControlEventTouchUpInside] takeUntil:photoCell.rac_prepareForReuseSignal] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self);
}];