ReactiveObjC使用简介

文是对ReactiveObjC部分使用介绍,原理及流程简介,见文章结尾

目录:

1、简单使用

2、UIKit (基于UIView控件)

3、Foundation (Foundation对象)

4、KVO (关于监听)

5、事件信号

6、结合网络请求使用

一、简单使用

RACSignal 信号相当于一个电视塔 ,只要将电视机调到跟电视塔的赫兹相同的频道,就可以收到信息。

subscribeNext 相当于订阅频道。当RACSignal信号发出sendNext消息时,subscribeNext就可以接收到信息。

//1、创建信号RACSignal*signal=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){//任何时候,都可以发送信号,可以异步[subscriber sendNext:@"发送信号"];//数据传递完,最好调用sendCompleted,这时命令才执行完毕。[subscriber sendCompleted];returnnil;}];//2、订阅信号RACDisposable*disposable=[signal subscribeNext:^(id  _Nullable x){//收到信号时NSLog(@"信号内容:%@",x);}];//取消订阅[disposable dispose];

1、调用 createSignal 创建一个信号

二、UIKit (基于UIView控件)

1、rac_textSignal 文本监听信号,可以减少对代理方法的依赖

//UITextField创建了一个 `textSignal`的信号,并订阅了该信号//当UITextField的内容发生改变时,就会回调subscribeNext[[self.textField rac_textSignal]subscribeNext:^(NSString*_Nullable x){NSLog(@"text changed = %@",x);}];

2、filter 对订阅的信号进行筛选

//当UITextField内输入的内容长度大于5时,才会回调subscribeNext[[[self.textField rac_textSignal]filter:^BOOL(NSString*_Nullable value){returnvalue.length>5;}]subscribeNext:^(NSString*_Nullable x){NSLog(@"filter result = %@",x);}];

3、ignore 对订阅的信号进行过滤

[[[self.textField rac_textSignal]ignore:@"666"]subscribeNext:^(NSString*_Nullable x){//当输入的内容 equalTo @"666" 时,这里不执行//其他内容,均会执行subscribeNextNSLog(@"ignore = %@",x);}];

4、rac_signalForControlEvents 创建事件监听信号

//当UIButton点击时,会调用subscribeNext[[self.button rac_signalForControlEvents:(UIControlEventTouchUpInside)]subscribeNext:^(__kindof UIControl*_Nullable x){NSLog(@"button clicked");}];

三、Foundation (Foundation对象)

1、NSNotificationCenter 通知

//@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];}

2、 interval定时器 (程序进入后台,再重新进入前台时,仍然有效,内部是用GCD实现的)

//创建一个定时器,间隔1s,在主线程中运行RACSignal*timerSignal=[RACSignal interval:1.0fonScheduler:[RACScheduler mainThreadScheduler]];//定时器总时间3秒timerSignal=[timerSignal take:3];//定义一个倒计时的NSInteger变量self.counter=3;@weakify(self)[timerSignal subscribeNext:^(id  _Nullable x){@strongify(self)self.counter--;NSLog(@"count = %ld",(long)self.counter);}completed:^{//计时完成NSLog(@"Timer completed");}];

3、delay延迟

//创建一个信号,2秒后订阅者收到消息[[[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@1];returnnil;}]delay:2]subscribeNext:^(id  _Nullable x){NSLog(@"delay : %@",x);}];

4、NSArray 数组遍历

NSArray*array=@[@"1",@"2",@"3",@"4",@"5"];[array.rac_sequence.signal subscribeNext:^(id  _Nullable x){NSLog(@"数组内容:%@",x);}];

5、NSDictionary字典遍历

NSDictionary*dictionary=@{@"key1":@"value1",@"key2":@"value2",@"key3":@"value3"};[dictionary.rac_sequence.signal subscribeNext:^(RACTuple*_Nullable x){// x 是一个元祖,这个宏能够将 key 和 value 拆开  乱序RACTupleUnpack(NSString*key,NSString*value)=x;NSLog(@"字典内容:%@ : %@",key,value);}];

6、RACSubject代理

定义一个DelegateView视图,并且声明一个RACSubject的信号属性,在touchesBegan方法中,给信号发送消息

@interfaceDelegateView:UIView//定义了一个RACSubject信号@property(nonatomic,strong)RACSubject*delegateSignal;@end@implementationDelegateView-(void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event{// 判断代理信号是否有值if(self.delegateSignal){// 有值,给信号发送消息[self.delegateSignal sendNext:@666];}}@end

在UIViewController中声明DelegateView作为属性

@interfaceViewController()@property(nonatomic,strong)DelegateView*bView;@end//使用前,记得初始化self.bView.delegateSignal=[RACSubject subject];[self.bView.delegateSignal subscribeNext:^(id  _Nullable x){//订阅到 666 的消息NSLog(@"RACSubject result = %@",x);}];

四、KVO (关于监听)

1、rac_valuesForKeyPath 通过keyPath监听

[[self.bView rac_valuesForKeyPath:@"frame"observer:self]subscribeNext:^(id  _Nullable x){//当self.bView的frame变化时,会收到消息NSLog(@"kvo = %@",x);}];

2、RACObserve 属性监听

//counter是一个NSInteger类型的属性[[RACObserve(self,counter)filter:^BOOL(id  _Nullable value){return[value integerValue]>=2;}]subscribeNext:^(id  _Nullable x){NSLog(@"RACObserve : value = %@",x);}];

在进行监听时,同样可以使用filter信号,对值进行筛选

3、RAC 事件绑定

//当UITextField输入的内容为@"666"时,bView视图的背景颜色变为grayColorRAC(self.bView,backgroundColor)=[self.textField.rac_textSignal map:^id_Nullable(NSString*_Nullable value){return[value isEqualToString:@"666"]?[UIColor grayColor]:[UIColor orangeColor];}];

#define RAC(TARGET, ...)这个宏定义是将对象的属性变化信号与其他信号关联,比如:登录时,当手机号码输入框的文本内容长度为11位时,"发送验证码" 的按钮才可以点击

五、事件信号

名词描述说明

RACTuple元祖只能存储OC对象 可以用于解包或者存储对象

bind包装获取到信号返回的值,包装成新值, 

再次通过信号返回给订阅者

concat合并按一定顺序拼接信号,当多个信号发出的时候,

有顺序的接收信号

then下一个用于连接两个信号,当第一个信号完成,

才会连接then返回的信号

merge合并把多个信号合并为一个信号,

任何一个信号有新值的时候就会调用

zipWith压缩把两个信号压缩成一个信号,

只有当两个信号都发出一次信号内容后,

并且把两个信号的内容合并成一个元组,

才会触发压缩流的next事件(组合的数据都是一一对应的)

combineLatest结合将多个信号合并起来,并且拿到各个信号的最新的值,

必须每个合并的signal至少都有过一次sendNext,

才会触发合并的信号

(combineLatest 与 zipWith不同的是,每次只拿各个信号最新的值)

reduce聚合用于信号发出的内容是元组,

把信号发出元组的值聚合成一个值,

一般都是先组合在聚合

map数据筛选map 的底层实现是通过 flattenMap 实现的

flattenMap信号筛选flattenMap 的底层实现是通过bind实现的

filter过滤过滤信号,获取满足条件的信号

ps:表格排版加上
换行之后,才不至于列的内容挤到一起,累累累...

1、RACTuple 元祖

只能存储OC对象 可以用于解包或者存储对象

//解包数据RACTupleUnpack(NSNumber*a,NSNumber*b)=x;

2、bind 包装

获取到信号返回的值,包装成新值, 再次通过信号返回给订阅者

[[self.textField.rac_textSignal bind:^RACSignalBindBlock _Nonnull{return^RACSignal*(id value,BOOL*stop){// 处理完成之后,包装成信号返回出去return[RACSignalreturn:[NSString stringWithFormat:@"hello: %@",value]];};}]subscribeNext:^(id  _Nullable x){NSLog(@"bind : %@",x);// hello: "x"}];

3、concat 合并

按一定顺序拼接信号,当多个信号发出的时候,有顺序的接收信号

RACSignal*signalA=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@"signalA"];[subscriber sendCompleted];returnnil;}];RACSignal*signalB=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@"signalB"];[subscriber sendCompleted];returnnil;}];// 把signalA拼接到signalB后,signalA发送完成,signalB才会被激活 顺序执行[[signalA concat:signalB]subscribeNext:^(id  _Nullable x){//先拿到 signalA 的结果 , 再拿到 signalB 的结果 , 执行两次NSLog(@"concat result = %@",x);}];

4、then 下一个

用于连接两个信号,当第一个信号完成,才会连接then返回的信号

// 底层实现  1.使用concat连接then返回的信号  2.先过滤掉之前的信号发出的值[[[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@1];[subscriber sendCompleted];returnnil;}]then:^RACSignal*{return[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){//可以对第一个信号的数据进行过滤处理 , 不能直接获得第一个信号的数据返回值[subscriber sendNext:@2];returnnil;}];}]subscribeNext:^(id x){// 只能接收到第二个信号的值,也就是then返回信号的值NSLog(@"then : %@",x);// 2}];

5、merge 合并

把多个信号合并为一个信号,任何一个信号有新值的时候就会调用

//创建多个信号RACSignal*mergeSignalA=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@1];returnnil;}];RACSignal*mergeSignalB=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@2];returnnil;}];// 合并信号,只要有信号发送数据,都能监听到.RACSignal*mergeSignal=[mergeSignalA merge:mergeSignalB];[mergeSignal subscribeNext:^(id x){//每次获取单个信号的值NSLog(@"merge : %@",x);}];

6、zipWith 压缩

把两个信号压缩成一个信号,只有当两个信号都发出一次信号内容后,并且把两个信号的内容合并成一个元组,才会触发压缩流的next事件(组合的数据都是一一对应的)

RACSignal*zipSignalA=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@1];[subscriber sendNext:@2];returnnil;}];RACSignal*zipSignalB=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){//3秒后执行dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3*NSEC_PER_SEC)),dispatch_get_main_queue(),^{[subscriber sendNext:@3];});//5秒后执行dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(5*NSEC_PER_SEC)),dispatch_get_main_queue(),^{[subscriber sendNext:@5];});returnnil;}];RACSignal*zipSignal=[zipSignalA zipWith:zipSignalB];[zipSignal subscribeNext:^(id  _Nullable x){// x 是一个元祖RACTupleUnpack(NSNumber*a,NSNumber*b)=x;NSLog(@"zip with : %@  %@",a,b);//第一次输出  1  3//第二次输出  2  5}];

7、combineLatest 结合

将多个信号合并起来,并且拿到各个信号的最新的值,必须每个合并的signal至少都有过一次sendNext,才会触发合并的信号 (combineLatest 与 zipWith不同的是,每次只拿各个信号最新的值)

RACSignal*combineSignalA=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@1];[subscriber sendNext:@2];returnnil;}];RACSignal*combineSignalB=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(3*NSEC_PER_SEC)),dispatch_get_main_queue(),^{[subscriber sendNext:@3];});dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(5*NSEC_PER_SEC)),dispatch_get_main_queue(),^{[subscriber sendNext:@5];});returnnil;}];RACSignal*combineSignal=[combineSignalA combineLatestWith:combineSignalB];[combineSignal subscribeNext:^(id  _Nullable x){// x 是一个元祖RACTupleUnpack(NSNumber*a,NSNumber*b)=x;NSLog(@"combineLatest : %@  %@",a,b);//第一次输出 2 3//第二次输出 2 5//因为combineSignalA中的2是最新数据,所以,combineSignalA每次获取到的都是2}];

8、reduce 聚合

用于信号发出的内容是元组,把信号发出元组的值聚合成一个值,一般都是先组合在聚合

RACSignal*reduceSignalA=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@1];returnnil;}];RACSignal*reduceSignalB=[RACSignal createSignal:^RACDisposable*_Nullable(id_Nonnull subscriber){[subscriber sendNext:@3];returnnil;}];RACSignal*reduceSignal=[RACSignal combineLatest:@[reduceSignalA,reduceSignalB]reduce:^id(NSNumber*a,NSNumber*b){//reduce中主要是对返回数据的处理  return[NSString stringWithFormat:@"%@ - %@",a,b];}];[reduceSignal subscribeNext:^(id  _Nullable x){//返回值x 取决于reduce之后的返回NSLog(@"reduce : %@",x);}];

9、map 数据过滤

map 的底层实现是通过flattenMap 实现的。map 直接对数据进行处理,并且返回处理后的数据

[[self.textField.rac_textSignal map:^id_Nullable(NSString*_Nullable value){// 当源信号发出,就会调用这个block,修改源信号的内容// 返回值:就是处理完源信号的内容。return[NSString stringWithFormat:@"hello : %@",value];}]subscribeNext:^(id  _Nullable x){NSLog(@"Map : %@",x);// hello: "x"}];

10、flattenMap 信号过滤

flattenMap 的底层实现是通过bind实现的。拿到原数据,处理完成之后,包装成信号返回

[[self.textField.rac_textSignal flattenMap:^__kindof RACSignal*_Nullable(NSString*_Nullable value){return[RACSignalreturn:[NSString stringWithFormat:@"hello : %@",value]];}]subscribeNext:^(id  _Nullable x){NSLog(@"flattenMap : %@",x);// hello "x"}];

10、filter 过滤

过滤信号,获取满足条件的信号

[[self.textField.rac_textSignal filter:^BOOL(NSString*value){returnvalue.length>6;}]subscribeNext:^(NSString*_Nullable x){NSLog(@"filter : %@",x);// x 值位数大于6}];

六、结合网络请求使用

以下网络接口均基于MVVM模式

1、请求单个接口

//创建请求接口的信号,该方法可以定义并实现在ViewModel层#pragmamark - 获取指定时间的课程+(RACSignal*)getCourseInfoByTime:(NSInteger)time{//此处为接口所需参数NSMutableDictionary*paramter=[NSMutableDictionary dictionary];[paramter setObject:@(time)forKey:@"exerciseTime"];return[RACSignal createSignal:^RACDisposable*(idsubscriber){//这里为接口请求方法 //接口url: CombinePath(TY_DEBUG_HOST, SPORT_HOME_COURSE) //postRequest:接口请求类型 post//paramter:参数//[SportCourseDataModel class]:接口返回类型model[Networking requestWithPath:CombinePath(TY_DEBUG_HOST,SPORT_HOME_COURSE)requestType:postRequest requestParamter:paramter responseObjctClass:[SportCourseDataModel class]completionBlock:^(BOOL isSuccess,id object,NSError*error){//当接口返回结果后,根据状态,分别传递object或者error给订阅者if(isSuccess){//将接口返回的接口object传递给subscribeNext[subscriber sendNext:object];//信号完成之后,最好调用sendCompleted[subscriber sendCompleted];}else{[subscriber sendError:error];}}];returnnil;}];}//信号订阅//在ViewController中定义一个方法,用来调用网络接口方法-(void)getCourseByIsExperience:(BOOL)isExperience{//使用 @weakify(self) 和 @strongify(self) 避免循环引用@weakify(self)[[SportViewModel getCourseInfoByTime:0isExperience:isExperience]subscribeNext:^(id  _Nullable x){@strongify(self)//接口请求成功,订阅者可以在这里获取到接口返回的内容 x}error:^(NSError*_Nullable error){@strongify(self)//当接口出错时,这里可以处理错误信息}];}

分析:

1、在ViewModel类中,创建了一个信号,这个信号请求了一个获取课程的接口。信号创建之后,并不会立即执行,要等订阅者,订阅并调用subscribeNext时,才会执行。

2、在ViewController中,经过用户操作,开始调用getCourseByIsExperience方法。此时,订阅者开始订阅信号,信号中的createSignal开始执行接口请求方法。

3、当接口请求成功后,根据状态,将对应的object或者error通过sendNext: 和sendError:传递给订阅者

4、订阅者开始执行subscribeNext 或者 error block中的代码

(ps:如果接口请求之后,不需要获取返回值,则可以在信号中这样返回 [subscriber sendNext:nil])

优点:这个接口请求过程,ViewController只需要将接口所需参数传入,即可得到接口的结果,大大简化了控制器层面的内容,使得控制器更加专注于页面之间的业务处理,数据传递等功能。

2、多个接口的同时调用 (以下的接口信号创建过程,不再描述)

//获取血压收缩的数据 接口信号RACSignal*systolicSignal=[DataStatisticsViewModel getItemDataByPersonId:personId baseItemId:self.systolicItemModel.baseItemId];//获取血压舒张压的数据 接口信号RACSignal*diastolicSignal=[DataStatisticsViewModel getItemDataByPersonId:personId baseItemId:self.diastolicItemModel.baseItemId];@weakify(self)//因为两个接口是需要同时获取到数据的,所以可以使用combineLatest组合信号[[RACSignal combineLatest:@[systolicSignal,diastolicSignal]]subscribeNext:^(RACTuple*_Nullable x){@strongify(self)//因为是请求了多个接口,所以会有多个数据返回,此处的x是一个元祖,所以使用RACTupleUnpack解包元祖//返回结果值(DataItemRecordModel)的顺序对应combineLatest中数组的信号顺序RACTupleUnpack(DataItemRecordModel*systolicModel,DataItemRecordModel*diastolicModel)=x;//这里可以直接使用返回值  systolicModel  和  diastolicModel}error:^(NSError*_Nullable error){@strongify(self)//没有数据[selfhandleTheErrorMessage:error];}];

多个接口同时调用的过程同单个接口请求类似。

需注意:

(一)多个接口同时请求时,只要有其中一个返回错误信息,整个结果即为失败,即会走error:^(NSError * _Nullable error){}这个block,所以必须多个接口都成功时,才会调用subscribeNext:^(RACTuple * _Nullable x){}block。

(二)可以对结果先做聚合处理,返回再返回结果,比如:

[[RACSignal combineLatest:@[systolicSignal,diastolicSignal]reduce:^id(DataItemRecordModel*systolicModel,DataItemRecordModel*diastolicModel){//reduce中对数据进行处理,可以将多个接口请求的数据,处理之后,统一返回一个结果returnsystolicModel;//也可以将处理完的数据包装成元祖返回RACTuple*tuple=RACTuplePack(systolicModel,diastolicModel);returntuple;}]subscribeNext:^(id  _Nullable x){//这里获取到reduce处理完成之后的数据}error:^(NSError*_Nullable error){//这里处理错误信息}];

以上为网络请求接口时的例子,更多的使用方式可以结合(5、事件信号)中的各种信号事件

你可能感兴趣的:(ReactiveObjC使用简介)