导语:
之前一直看我师父在研究响应式编程,但是在项目中一直没有用到,然后我也懒得看,(对,我没有上进心😆)。这段时间闲来无事,简单的研究了一下,百闲之中整理出了文档。
ReactiveCocoa,很神奇的一个框架,为我们提供了很多方便的方法,去处理事件的操作,如UITextField不需要实现其代理方法就可以获取到输入框中的内容。RAC的主要好处是它提供了一个信号RACSignal类,来统一处理Cocoa的各种行为,包括delegate,block,target-action机制,notification和KVO等等,可以把要处理的事件和监听的代码放在一起,使用起来也及其简便易懂。
它在5.0以后详细的拆分为了四个库:ReactiveCocoa、ReactiveSwift、ReactiveObjC、ReactiveObjCBridge。
其中ReactiveCocoa适用于纯Swift项目;
ReactiveObjC适用于纯OC项目;
本文主要讲下ReactiveObjC的应用,swift目前项目中还没有普及,等普及之后再研究啦。
导入
1.手动导入,github上都有地址,自己去下载导入 (https://github.com/ReactiveCocoa/ReactiveCocoa)
2.pods导入,这里不是导入 ReactiveCocoa
,而是ReactiveObjC
,一定不要导入错了
导入注意事项
1.若项目为Swift,则导入ReactiveCocoa
2.若项目为Swift,则导入ReactiveObjC
3.若项目为Swift和OC混编,则需要将ReactiveObjC和ReactiveCocoa都导入,同时需导入ReactiveObjCBridge,3个库都是可以用pods导入的
推荐使用pods
platform: 'ios', '8.0'
pod 'ReactiveObjC'
使用
不管是ReactiveCocoa还是ReactiveObjC,其核心就是信号,也就是事件流。
一.创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//发送信号
[subscriber sendNext:@"我要发送信号啦"];
//如果不再发送信号,最好发送信号完成,内部会自动调用[RACdisposable disposable]取消订阅信号
[subscriber sendCompleted];
return nil;
}];
//订阅信号, 才会激活信号,信号激活了,才会发出去,输出的信号为 “我要发送信号啦”
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"订阅信号:%@", x);
}];
//或者利用block回调
RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//发送信号
[subscriber sendNext:@"发送信号2"];
//如果不再发送信号,最好发送信号完成,内部会自动调用[RACdisposable disposable]取消订阅信号
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
//block调用时刻,当信号发送完成后或者发送错误,就会自动执行这个block,取消订阅信号
//执行完block后,当前信号就不在被订阅了
NSLog(@"信号被销毁");
}];
}];
二.RACSubject使用
//创建信号
RACSubject *subject = [RACSubject subject];
//发送信号
[subject sendNext:@"发送信号"];
//订阅信号
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"信号内容%@", x);
}];
三.RACReplaySubject使用
//1.创建信号
RACReplaySubject *replaySub = [RACReplaySubject subject];
//2.创建订阅者
[replaySub subscribeNext:^(id _Nullable x) {
NSLog(@"哈哈");
}];
//3.发送订阅者信号
[replaySub sendNext:@"123456"];
//RACReplaySubject 发送数据
//1.保存值
//2.遍历所有的订阅者,发送数据
//3. 第3条可以放在第2条的前面,即可以先发送,再订阅。RACSubject不可以调整
//RACReplaySubject与RACSubject区别:RACReplaySubject可以先发送信号,在订阅信号,RACSubject就不可以。
//使用场景一:如果每个信号被重复订阅一次,就需要把之前的值重复发送一次,使用重复提供信号
//使用场景二:可以设置capacity数量来限制缓存的value的数量,即只缓冲最新的几个
四.RACTuple元组
RAC的元祖,跟我们OC的数组其实是一样的,它其实就是封装了我们OC的数组
//创建元祖
RACTuple *tuple1 = [RACTuple tupleWithObjects:@"one", @"two", @"three", @"four", nil];
//从别的数组中获取内容
NSArray *array = @[@"five", @"six", @"seven", @"eight"];
RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:array];
//利用RAC宏快速封装,即把数据包装成元组
RACTuple *tuple3 = RACTuplePack(@"nine", @"ten", @"eleven", @"twelve");
NSLog(@"取第一个内容1:%@", tuple1[0]);
NSLog(@"取第一个内容2:%@", [tuple2 first]);
NSLog(@"取第一个内容3:%@", [tuple3 last]);
五.RACTupleUnpack
把RACTuple元组类解包成对应的数据
//解包元组,会把元组的值,按照顺序赋给参数里面的变量赋值,即name值为Damon,age值为33
RACTuple *tuple4 = RACTuplePack(@"Damon", @"33");
RACTupleUnpack(NSString *name, NSString *age) = tuple4;
六.遍历ARRAY数组和Dictionary字典
遍历ARRAY数组和Dictionary字典
NSArray *arrayFruit = @[@"apple", @"banana", @"watermelon", @"lemon"];
[arrayFruit.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"数组内容:%@", x);
}];
NSDictionary *dictionary = @{@"红色":@"red", @"绿色":@"green", @"紫色":@"purple", @"青色":@"cyan",};
[dictionary.rac_sequence.signal subscribeNext:^(id _Nullable x) {
//普通用法
NSString *key1 = x[0];
NSString *value1 = x[0];
NSLog(@"%@--%@", key1, value1);
//高级用法
//x 是一个元祖,这个宏能够将key 和 value拆开
RACTupleUnpack(NSString *key, NSString *value) = x;
NSLog(@"字典内容%@:%@", key,value);
}];
或者使用RAC集合,即数组转RAC集合
RACSequence *sequence = arrayFruit.rac_sequence;
//把集合转为信号
RACSignal *racSignal = sequence.signal;
//订阅集合信号,内部会自动遍历所有的元素发出来
[racSignal subscribeNext:^(id _Nullable x) {
NSLog(@"11--%@", x);
}];
七.数组内容全部替换为0
第一个是单个操作,第二个是一次性全部置换,2个方法都不会改变原数组内容,操作完成后都会生成一个新的数组,省去了创建可变数组然后遍历出来单个添加的操作
/*内容单个替换*/
NSArray *aryChange1 = @[@"11", @"22", @"33", @"44"];
NSArray *newAryChange1 = [[array.rac_sequence map:^id _Nullable(id _Nullable value) {
//将所有内容替换为0
return @"0";
}] array];
NSLog(@"单个替换的数组为:%@", newAryChange1);
/*内容一次性替换*/
NSArray *aryChange2 = @[@"111", @"222", @"333", @"444"];
NSArray *newAryChange2 = [[aryChange2.rac_sequence mapReplace:@"0"] array];
NSLog(@"一次性替换的数组为:%@", newAryChange2);
八.监听UITextField的输入改变
可以省去设置delegate 和实现代理方法的步骤
//监听TextField的输入(内容改变就会调用)
UITextField *textField = [[UITextField alloc] init];
textField.frame = CGRectMake(20, 100, 200, 60);
textField.backgroundColor = [UIColor grayColor];
[self.view addSubview:textField];
RACSignal *signalText = [textField rac_textSignal];
[signalText subscribeNext:^(id _Nullable x) {
NSLog(@"输入框内容1:%@", x);
}];
//或者添加监听条件
[[textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
//表示文字长度 大于5时才会调用下面的block
return value.length > 5;
}] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"输入框内容2%@", x);
}];
九.监听button事件
可以省去addTarget添加事件和创建方法的步骤
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(10, 200, 50, 50);
btn.backgroundColor = [UIColor greenColor];
[self.view addSubview:btn];
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
//点击时触发的事件
NSLog(@"按钮被点击了 %@", x);
}];
十.登录按钮状态实时监听
下面表示只有用户名和密码输入框内容都大于0时,登录按钮才可以点击,而且状态是实时监听的,一句代码就能完成这个功能
RAC(loginBtn, enabled) = [RACSignal combineLatest:@[user.rac_textSignal, psd.rac_textSignal] reduce:^id _Nonnull{
return @(user.text.length && psd.text.length);
}];
十一.监听notification通知事件
可以省去在-(void)dedalloc{}中清除通知和监听通知创建方法的步骤
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"键盘弹起 %@", x);
}];
十二.代替delegate代理方法
可以省去监听以及设置delegate的步骤,下面表示只要view中执行了clickMe这个方法,就会发送信号执行这个回调
UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
[[button rac_signalForSelector:@selector(clickMe)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"按钮被点击了");
}];
十三.代替KVO监听
可以代替KVO监听,下面表示把监听view的frame属性改变转换成信号,只要值改变就会发送信号
UILabel *label = [[UILabel alloc] init];
label.frame = CGRectMake(120, 350, 100, 40);
label.text = @"我是label";
[self.view addSubview:label];
// OC 写法
// [label addObserver:self forKeyPath:@"age" options:NSKeyValueObservingOptionNew context:nil];
[[label rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id _Nullable x) {
NSLog(@"label的属性改变了:%@", x);//x是监听属性的改变结果
}];
//或者这样写
// [RACObserve(label, frame) subscribeNext:^(id _Nullable x) {
// NSLog(@"属性的改变 %@", x);
// }];
十四.代替NSTimer计时器
self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"当前时间:%@", x);
//关闭计时器
[self.disposable dispose];
}];
以上基本上就是RAC常用的方法,
参考文档:
1.https://www.jianshu.com/p/0845b1a07bfa
2.https://www.jianshu.com/p/68ae979ba814?utm_campaign=maleskine&utm_content=note&utm_medium=seo_notes&utm_source=recommendation