ReactiveObjC (一般来说又叫做ReactiveCocoa或者RAC)是一个基于响应式编程的Objective-C的框架。使用起来真的很强大,可以用来代替监听事件方法,代替KVO,代替通知代理等。
一.使用
使用pods导入:
pod 'ReactiveObjC', '~> 3.1.0'
二.流程分析
RAC核心思想:创建信号-订阅信号-发送信号,主要有三个关键类:
1、RACSignal
信号RACSignal
是各种信号的基类,其中RACDynamicSignal
是用的最多的动态信号
2、RACSubscriber
订阅者RACSubscriber
是实现了RACSubscriber
协议的订阅者类,这个协议定义了4个必须实现的方法
RACDisposable
清洁工RACDisposable
主要是对资源的释放处理,其中使用RACDynamicSignal
时,会创建一个RACCompoundDisposable
管理清洁工对象。其内部定义了两个数组,一个是_inlineDisposables[2]
固定长度2的A fast array
,超出2个对象的长度由_disposables
数组管理,_inlineDisposables
数组速度快,两个数组都是线程安全的。rac_textSignal
文本监听信号,可以减少对代理方法的依赖UITextField*field = [[UITextField alloc]initWithFrame:CGRectMake(50, 50, 200, 50)];
field.backgroundColor = [UIColor grayColor];
[self.view addSubview:field];
//UITextField创建了一个 `textSignal`的信号,并订阅了该信号
//当UITextField的内容发生改变时,就会回调subscribeNext
[[field rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"text changed = %@", x);
}];
2、filter
对订阅的信号进行筛选
[[field.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
return value.length > 5;
}] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"输入框内容%@",x);
}];
3、ignore
对订阅的信号进行过滤
[[field.rac_textSignal ignore:@"666"] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"输入框内容%@",x);
}];
rac_signalForControlEvents
创建事件监听信号(UIButton)//创建一个按钮
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 70, 70)];
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
btn.tag = 1001;
//监听点击事件
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"按钮点击了");
}];
5、代替KVO
//创建一个按钮
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 70, 70)];
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
btn.tag = 1001;
//监听点击事件
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"按钮点击了");
x.frame = CGRectMake(100, 100, 200, 200);
}];
[[btn rac_valuesAndChangesForKeyPath:@"frame" options:(NSKeyValueObservingOptionNew) observer:self] subscribeNext:^(RACTwoTuple<id,NSDictionary *> * _Nullable x) {
NSLog(@"frame改变了%@",x.second);
}];
或者使用如下方法,#define RAC(TARGET, ...)
这个宏定义是将对象的属性变化信号与其他信号关联
[RACObserve(btn, frame) subscribeNext:^(id _Nullable x) {
NSLog(@"frame改变了%@",x);
}];
6、代替通知
//@property (nonatomic, strong) RACDisposable *keyboardDisposable;
self.keyboardDisposable = [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardDidShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"%@ 键盘弹起", x); // x 是通知对象
}];
注意:rac_addObserverForName
同样需要移除监听。RAC通知监听会返回一个RACDisposable清洁工的对象,在dealloc中销毁信号,信号销毁时,RAC在销毁的block中移除了监听
- (void)dealloc {
[_keyboardDisposable dispose];
}
7、代替timer循环执行
[[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"%@",x);
}];
8、数组遍历
NSArray*array = @[@"1", @"2", @"3", @"4", @"5"];
[array.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"数组内容:%@",x);
}];
9、字典遍历
NSDictionary*dictionary = @{@"key1":@"value1", @"key2":@"value2", @"key3":@"value3"};
[dictionary.rac_sequence.signal subscribeNext:^(id _Nullable x) {
// NSLog(@"字典内容:%@",x);
RACTupleUnpack(NSString*key,NSString*value) = x;
NSLog(@"字典内容:%@:%@",key,value);
}];
10、代替代理
新建一个自定义view——RACView,重写初始化方法。
在RACView.h文件中定义一个rac信号属性
@property (nonatomic,strong)RACSubject *btnClickSingle;
在.m文件中重写初始化方法,发送信号
-(instancetype)initWithFrame:(CGRect)frame{
if (self = [super initWithFrame:frame]) {
self.backgroundColor = [UIColor redColor];
//创建一个按钮
UIButton *btn = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 70, 70)];
btn.backgroundColor = [UIColor redColor];
[self addSubview:btn];
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
//发送信号
[self.btnClickSingle sendNext:@"按钮点击咯"];
}];
}
return self;
}
-(RACSubject *)btnClickSingle{
if (!_btnClickSingle) {
_btnClickSingle = [RACSubject subject];
}
return _btnClickSingle;
}
在ViewController中
//只能代替没有返回值的代理
-(void)demo3{
RACView *racView = [[RACView alloc]initWithFrame:CGRectMake(0, 0, 300, 300)];
[self.view addSubview:racView];
//替代了代理把值从racView中传了过来。
[racView.btnClickSingle subscribeNext:^(id _Nullable x) {
//要传多个值,,可以传一个集合
NSLog(@"%@",x);
}];
}
11.用来给某个对象的某个属性绑定信号,只要产生信号内容,就把内容给属性赋值
UITextField*field = [[UITextField alloc]initWithFrame:CGRectMake(50, 120, 200, 50)];
field.backgroundColor = [UIColor greenColor];
[self.view addSubview:field];
UILabel*label = [[UILabel alloc]initWithFrame:CGRectMake(50, 50, 200, 50)];
[self.view addSubview:label];
RAC(label,text) = field.rac_textSignal;
12.登录按钮的状态根据输入框内容改变
UITextField*userNameTF = [[UITextField alloc]initWithFrame:CGRectMake(40, 70, 200, 50)];
UITextField*passwordTF = [[UITextField alloc]initWithFrame:CGRectMake(40, 130, 200, 50)];
userNameTF.placeholder = @"请输入用户名";
passwordTF.placeholder = @"请输入密码";
[self.view addSubview:userNameTF];
[self.view addSubview:passwordTF];
UIButton*loginBtn = [[UIButton alloc]initWithFrame:CGRectMake(40, 180, 200, 50)];
[loginBtn setTitle:@"马上登录" forState:UIControlStateNormal];
[self.view addSubview:loginBtn];
RAC(loginBtn, enabled) = [RACSignal combineLatest:@[userNameTF.rac_textSignal, passwordTF.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
return @(username.length >= 11 && password.length >= 6);
}];
RAC(loginBtn,backgroundColor) = [RACSignal combineLatest:@[userNameTF.rac_textSignal, passwordTF.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
return (username.length >= 11 && password.length >= 6) ? [UIColor redColor] : [UIColor grayColor];
}];
13.避免循环引用,外部用@weakify(self),内部用@strongify(self)
// @weakify() 宏定义
@weakify(self) //相当于__weak typeof(self) weakSelf = self;
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self) //相当于__strong typeof(weakSelf) strongSelf = weakSelf;
NSLog(@"%@",self.view);
return nil;
}];
_signal = signal;
参考链接:https://www.jianshu.com/p/14075b5ec5ff
https://www.jianshu.com/p/6af75a449d90
原理及流程简介:https://www.jianshu.com/p/fecbe23d45c1