RAC简介
- 在RAC中,万物皆信号。
- RAC 指的就是 RactiveCocoa ,是 Github 的一个开源框架,能够通过信号提供大量方便的事件处理方案,让我们更简单粗暴地去处理事件,现在分为 ReactiveObjC(OC) 和 ReactiveSwift(swift),官方的说,ReactiveCocoa(其简称为RAC)是由GitHub开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程和响应式编程的特性。。
- 团队协作时,必须注意一个点,对于很熟悉RAC的人来说,使用RAC是非常方便的。但对于不熟悉RAC的人来说,由于RAC的可阅读性是很差的,所以需耗费大量时间阅读和学习。
- 未避免循环引用,需使用@weakify(self),@strongify(self)。这两个宏至少是一对出现的
- RAC就是一个第三方库,他可以大大简化你的代码过程。
RAC架构框架图
RAC架构框架图 信号流程
一 、使用
老规矩,pod install
pod 'ReactiveObjC', '~> 3.0.0' //建议纯oc代码使用ReactiveObjC
1、基本控件
- UITextField
//监听文本输入
[[_textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"%@",x);
}];
//可根据自己想要监听的事件选择
[[_textField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"%@",x);
}];
//添加条件 -- 下面表示输入文字长度 > 10 时才会调用subscribeNext
[[_textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
return value.length > 10;
}] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"输入框内容:%@", x);
}];
- UIButton
//监听按钮点击事件
[[View.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"-->%@",x);
}];
//这种不建议使用,暂时不知道didBtnAction方法不写会报警高~不过可以也实现
// [[View.btn rac_signalForSelector:@selector(didBtnAction:)]subscribeNext:^(id x) {
// NSLog(@"-->%@",x);
// } completed:^{
// }];
rac 类似代理传值例子
//第一
@property (strong,nonatomic) RACSubject *btnSubject;
//第二,在View点m,按钮点击方法里面
// view里面带有btn,点击的时候
- (void)didBtnAction:(UIButton *)btn{
SWLog(@"回到控制器后g要干嘛?");
[self.btnSubject sendNext:@{@"text":@"这边可以携带参数"}];
}
- (RACSubject *)btnSubject{
if (!_btnSubject) {
_btnSubject = [RACSubject subject];
}
return _btnSubject;
}
//控制器外部调用
[View.btnSubject subscribeNext:^(id x) {
SWLog(@"收到!==%@",x);
// log==> @{@"text":@"这边可以携带参数"}
} completed:^{
}];
- 计时器(interval、delay)
/*
声明:RACDisposable *disposable
创建一个RACDisposable,调用disposable方法等时候就会进入创建对象的block,把定时器释放掉
这两个宏就是为了解决循环引用的,且必须配套使用。
@weakify(self)
@strongify(self)
相当于下面:
__weak typeof(self) weakSelf = self;
__strong typeof(weakSelf) strongSelf = weakSelf;
*/
//类似timer
@weakify(self)
self.disposable = [[RACSignal interval:2 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
@strongify(self)
NSLog(@"时间:%@", x); // x 是当前的时间
//关闭计时器
[self.disposable dispose];
}];
//延时
[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"延时2秒"];
return nil;
}] delay:2] subscribeNext:^(id x) {
NSLog(@"-->%@",x);
}];
2.KVO 属性监听
//方法一
[_redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
NSLog(@"方法一 %@",value);
}];
//方法二
[[_redView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id _Nullable x) {
NSLog(@"方法二 %@",x);
}];
//方法三 宏写法
[RACObserve(_redView, frame) subscribeNext:^(id _Nullable x) {
NSLog(@"方法三%@",x);
}];
3.textField监听,lable赋值,手势
//此处RAC宏相当于让_label订阅了_textField的文本变化信号
//赋值给label的text属性
RAC(_label, text) = _textField.rac_textSignal;
//快速添加手势
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
[[tap rac_gestureSignal] subscribeNext:^(id x) {
} completed:^{
}];
[newTextLable addGestureRecognizer:tap];
//输入框监听
[[self.textFild rac_textSignal] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
4.监听 Notification 通知事件
//通知常用
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"通知名字" object:nil]subscribeNext:^(id x) {
} completed:^{
}];
5.多个订阅 RACMulticastConnection
- (void)sss{
RACSignal *signal=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"123444"];
return nil;
}];
RACMulticastConnection *mut = [signal publish];
[mut.signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
[mut.signal subscribeNext:^(id _Nullable x) {
NSLog(@"22%@",x);
}];
[mut connect];
}
6.绑定信号 bind
- (void)text{
RACSubject *signal = [RACSubject subject];
[[signal bind:^RACStreamBindBlock _Nonnull{
return ^RACSignal *(id value, BOOL* stop){
NSLog(@"%@",value);
NSString *v = [NSString stringWithFormat:@"处理过的数据%@",value];
return [RACReturnSignal return:v];//需要导入头文件#import "RACReturnSignal.h"
};
}] subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
[signal sendNext:@"1111"];
//输出
//1111
//处理过的数据1111
}
7.映射
- (void)text{
//映射
RACSubject *subject = [RACSubject subject];
[[subject map:^id _Nullable(id _Nullable value) {
return [NSString stringWithFormat:@"处理过的数据%@",value];
}] subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
} ];
[subject sendNext:@"11"];
//输出
//处理过的数据11
}
8.忽略 ignore
- (void)text2{
RACSubject * subject = [RACSubject subject];
[[subject ignore:@"a"] subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
[subject sendNext:@"a"];
[subject sendNext:@"b"];
[subject sendNext:@"c"];
//输出log
// 2019-03-07 14:35:00.348048+0800 MVVM+RAC[10199:763418] b
// 2019-03-07 14:35:00.348200+0800 MVVM+RAC[10199:763418] c
}
9 RACTuple、RACSequence
1.RACTuple元组类,类似NSArray,用来包装值.
2.RACSequence:RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
// 1.遍历数组
NSArray *numbers = @[@1,@2,@3,@4];
// 这里其实是三步
// 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
// 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
// 第三步: 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。
[numbers.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
// 2.遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)
NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// 解包元组,会把元组的值,按顺序给参数里面的变量赋值
RACTupleUnpack(NSString *key,NSString *value) = x;
// 相当于以下写法
// NSString *key = x[0];
// NSString *value = x[1];
NSLog(@"%@ %@",key,value);
}];
// 3.字典转模型
// 3.1 OC写法
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
NSMutableArray *items = [NSMutableArray array];
for (NSDictionary *dict in dictArr) {
FlagItem *item = [FlagItem flagWithDict:dict];
[items addObject:item];
}
// 3.2 RAC写法
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
NSMutableArray *flags = [NSMutableArray array];
_flags = flags;
// rac_sequence注意点:调用subscribeNext,并不会马上执行nextBlock,而是会等一会。
[dictArr.rac_sequence.signal subscribeNext:^(id x) {
// 运用RAC遍历字典,x:字典
FlagItem *item = [FlagItem flagWithDict:x];
[flags addObject:item];
}];
NSLog(@"%@", NSStringFromCGRect([UIScreen mainScreen].bounds));
// 3.3 RAC高级写法:
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
// map:映射的意思,目的:把原始值value映射成一个新值
// array: 把集合转换成数组
// 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。
NSArray *flags = [[dictArr.rac_sequence map:^id(id value) {
return [FlagItem flagWithDict:value];
}] array];
10 项目开发- RACCommand
RACCommand:RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。
模拟实际开发中,一个获取书架的接口传参数和请求:
1.在viewModel.h 里面声明一个属性
/// 获取书架
@property (nonatomic , strong) RACCommand *get_list_bookcaseCommand;
2.在viewModel.m 进行懒加载实现
#pragma mark - get
- (RACCommand *)get_list_bookcaseCommand
{
if (_get_list_bookcaseCommand== nil) {
kWeakObject(self)
_get_list_bookcaseCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
kStrongObject(self)
[CPNetManager POST:get_list_bookcaseUrl parameters:input success:^(NSDictionary * _Nullable responseObject, NSURLSessionDataTask * _Nonnull task) {
[subscriber sendNext:responseObject];
[subscriber sendCompleted];
} failure:^(NSDictionary * _Nullable responseObject, NSString * _Nullable message, NSString * _Nullable code, NSURLSessionDataTask * _Nonnull task) {
[subscriber sendNext:nil];
[subscriber sendCompleted];
}];
return nll;
}];
}];
}
return _get_list_bookcaseCommand;
}
3.在controller 进行懒加载viewModel对象再需要请求的作用域去调用传参数和信用监听。
//self.viewModel.get_list_bookcaseCommand 为RACCommand对象 发送参数进行请求,input为参数
[self.viewModel.get_list_bookcaseCommand execute:@{@"page":@"1",@"uid”: userModel.uid}];
//获取书架 [self.viewModel.get_list_bookcaseCommand.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
kStrongObject(self)
[self.viewModel.headRefresh endRefreshing];
[self.viewModel.footRefresh endRefreshing];
if (x)
{
//收到数据x 进行解析
self.dataModel.model = [HBHomeModel yy_modelWithJSON:x[@"data"]];
[self.viewModel.collectionView reloadData];
[self.viewModel.footRefresh endRefreshingWithNoMoreData];
}
}];
11、ReactiveCocoa开发中常见用法。
11.1 代替代理:
rac_signalForSelector:用于替代代理。
11.2 代替KVO :
rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变。
11.3 监听事件:
rac_signalForControlEvents:用于监听某个事件。
11.4 代替通知:
rac_addObserverForName:用于监听某个通知。
11.5 监听文本框文字改变:
rac_textSignal:只要文本框发出改变就会发出这个信号。
11.6 处理当界面有多次请求时,需要都获取到数据时,才能展示界面
rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组),每一个signal都至少sendNext过一次,就会去触发第一个selector参数的方法。
使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。
12 、项目开发-短信倒计时、登录按钮是否可点击
@property (nonatomic, strong) RACDisposable * disposable;
@property (nonatomic, assign) NSInteger time;
@weakify(self)
[[_sendButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self)
self.time = 10;
self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
@strongify(self)
self.time -- ;
NSString *text = (self.time > 0) ? [NSString stringWithFormat:@"请稍等%zd秒",self.time] : @"重新发送";
[self.sendButton setTitle:text forState:UIControlStateNormal];
self.sendButton.enabled = (self.time == 0);
if (self.time == 0) [self.disposable dispose]; //关掉信号
}];
}];
//根据textfield的内容来改变登录按钮的点击可否
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);
}];
//根据textfield的内容来改变登录按钮的背景色
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];
}];
网友评论