简介
ReactiveCocoa(RAC):RAC是一套基于Cocoa的FRP(Functional Reactive Programming:函数响应式编程)框架。
安装:可通过CocoaPods: pod "ReactiveObjC"
思想
学习一种新的编程范式,困难不在于一门编程语言,而是怎么学会用另一种方式去思考。思路比语法更重要。
FRP(Functional Reactive Programming)函数响应式编程,这是RAC的核心思想。FRP是一种编程范式,是组合了函数式编程和响应式编程范式。接下来熟悉一下这两个编程范式。
函数式编程
如何理解?
如果说面向对象是对数据的抽象,那么函数式编程就是对行为的抽象。
就是允许把函数本身作为参数传入另一个函数,还允许返回一个函数!它的核心是函数。在解决问题时,使用不可变值和函数,函数对一个值进行处理,映射成另一个值。
关键点: 参数是个函数 返回值也是函数
现实生活中行为:
洗车:可以将函数比作一个自动洗车间,脏的汽车进去,自动洗完后,出来还是汽车,只是汽车变干净了。
响应式编程
如何理解?
面向数据流的编程思想,类似观察者模式一样的表现。
计算是前后呼应的,相互关联,有些变化了,彼此之间的关系也会随着值的变化而变化。
现实生活中行为:
防火:室内烟雾散发到一定程度时,烟感探测器探测到安全极限,及时发出失火警报。
关键点: 相互关联、跟随变化
开始学习RAC
RAC中Signal的基本的订阅过程
-
创建信号
-
订阅信号
-
发送信号
-
取消信号
RAC常用的类
RACSignal
RACSignal(冷信号)继承于抽象类RACStream,是RAC的核心类,信号只有被订阅时才会送出信号值。
冷信号特点:
- 不可变;
- 被动;
创建冷信号
在RACSignal.h中的声明的createSignal类方法如下:
+ (RACSignal<ValueType> *)createSignal:(RACDisposable * _Nullable (^)(id<RACSubscriber> subscriber))didSubscribe RAC_WARN_UNUSED_RESULT;
createSignal 类方法说明:
- 作用:创建新的信号
- 参数:didSubscribe,是一个block类型变量,didSubscribe block变量自身是一个类似C语言的函数指针变量,didSubscribe 指向的函数包含一个RACSubscriber 参数,返回一个RACDisposable 对象。
- 返回值:返回一个RACSignal类型的信号。
例:
//创建信号
RACSignal *singalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
//发送信号
[subscriber sendNext:@"1"];
[subscriber sendNext:@(2)];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"------this is RACDisposable---------");
}];
}];
//订阅信号
[singalA subscribeNext:^(id _Nullable x) {
NSLog(@"-------singalA value is %@-------",x);
}];
返回结果如下:
2021-04-10 22:10:30.127793+0800 RACDemo[14148:238689] -------singalA value is 1-------
2021-04-10 22:10:30.127938+0800 RACDemo[14148:238689] -------singalA value is 2-------
2021-04-10 22:10:30.128098+0800 RACDemo[14148:238689] ------this is RACDisposable---------
上面的过程拆分一下可以这样写:
//声明一名称为didSubscribe的block变量,带有一个RACSubscriber类型参数,返回一个RACDisposable类型对象
RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
//为didSubscribe定义,(block的实现)
didSubscribe = ^RACDisposable * (id<RACSubscriber> sub){
//发送信号
[sub sendNext:@"1"];
[sub sendNext:@(2)];
[sub sendCompleted];
[sub sendError:nil];
return [RACDisposable disposableWithBlock:^{
NSLog(@"----didSubscribe--RACDisposable--------");
}];
};
//创建信号
RACSignal *signalB = [RACSignal createSignal:didSubscribe];
//订阅信号
[signalB subscribeNext:^(id _Nullable x) {
NSLog(@"------signalB x is %@--------",x);
}];
返回结果如下:
2021-04-10 22:13:58.758873+0800 RACDemo[14258:241973] ------signalB x is 1--------
2021-04-10 22:13:58.758993+0800 RACDemo[14258:241973] ------signalB x is 2--------
2021-04-10 22:13:58.759078+0800 RACDemo[14258:241973] ----didSubscribe--RACDisposable-------
RACSubscriber
订阅者:负责发信号,本身是个协议;只要遵循它,并且实现协议内的方法,就会成为订阅者。
常用的几个方法:
//发送值到订阅者
- (void)sendNext:(nullable id)value;
//发送错误给订阅者
- (void)sendError:(nullable NSError *)error;
//告诉订阅者发送完成了
- (void)sendCompleted;
RACDisposable
取消订阅或清理资源,发送失败或者完成,会自动触发取消订阅方法。
//
+ (instancetype)disposableWithBlock:(void (^)(void))block;
//取消订阅
- (void)dispose;
RACSubject
RACSubject(热信号)t继承于RACSignal,一个subject可认为是一个信号,你可以手动控制sendNext、sendError、sendCompleted事件。可以帮助你将非RAC桥接到RAC上。
热信号特点:
-
可变;
-
主动;
-
可以桥接RAC和非RAC;
RACSubject 对外公开的接口只有两个,一个是创建一个热信号对象,另一个是发送消息的方法。
/// Returns a new subject.
+ (instancetype)subject;
// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value;
创建热信号的接口:
+ (instancetype)subject;
这个方法创建的时候,会初始化两个私有属性,一个是subscribers数组 、另一个是disposable。
subscribers:数组里包含了所有的当前订阅者,在self的同步操作时候会使用它遍历所属的订阅者。
disposable:包含接收者对其他信号的所有订阅。主要是用于取消订阅和清理资源的操作。
发送信号的接口:
- (void)sendNext:(nullable ValueType)value;
重新定义了RACSubscriber协议的sendNext方法。
创建热信号
例:创建一个热信号
//创建信号
RACSubject *subject=[RACSubject subject];
//订阅信号
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"subject x is [%@]",x);
}];
//发送信号
[subject sendNext:@"123"];
//结束订阅
[subject sendCompleted];
返回结果如下:
2021-04-13 16:48:52.043076+0800 RACDemo[29017:264320] subject x is [123]
常用高阶函数
绑定 bind
作用: 可将信号拦截后进行过滤再发送给新的信号,类似消息转发
typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);
- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block RAC_WARN_UNUSED_RESULT;
例:
// //创建信号
RACSubject *subject = [RACSubject subject];
//绑定新的信号
RACSignal *singnal=[subject bind:^RACSignalBindBlock _Nonnull {
return ^RACSignal*(id value,BOOL *stop){
return[RACReturnSignal return:value];
};
}];
// 订阅信号
[singnal subscribeNext:^(id _Nullable x) {
NSLog(@"bind x is [%@]",x);
}];
//发送信号
[subject sendNext:@"send bind value 123"];
返回结果如下:
2021-04-12 10:16:57.562421+0800 RACDemo[4075:59403] bind x is [send bind value 123]
合并
concat
作用:按顺序合并多个信号为一个信号,其中前一个信号必须发送sendCompleted表示执行完成,才会执行后面一个信号,类似串行队列,先完成一个请求,才进行下一个请求。
- (RACSignal *)concat:(RACSignal *)signal RAC_WARN_UNUSED_RESULT;
例:
//创建信号
RACSubject *subject=[RACSubject subject];
//创建信号
RACSubject *subject2=[RACSubject subject];
//连接信号
RACSignal *signal=[subject concat:subject2];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"concat x is [%@]",x);
}];
//发送信号
[subject sendNext:@"abc"];
//subject 信号执行完成,必须加上 sendCompleted,表示信号结束了,否则无法连接下一个信号
[subject sendCompleted];
//subject2发送信号
[subject2 sendNext:@"ABC"];
返回结果如下:
2021-04-12 16:42:14.281303+0800 RACDemo[17888:262262] concat x is [abc]
2021-04-12 16:42:14.281532+0800 RACDemo[17888:262262] concat x is [ABC]
merge
作用:按顺序合并多个信号为一个信号,其中前一个信号不添加sendCompleted,也会执行后面一个信号。只要有一个信号来,合并操作就会接受。不强调先完成一个再进行下一个。
例:
//创建信号
RACSubject *subject=[RACSubject subject];
//创建信号
RACSubject *subject2=[RACSubject subject];
//连接信号
RACSignal *signal=[subject merge:subject2];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"merge x is [%@]",x);
}];
//发送信号
[subject sendNext:@"abc"];
//subject2发送信号
[subject2 sendNext:@"ABC"];
返回结果如下:
2021-04-12 17:08:23.410822+0800 RACDemo[19726:287122] merge x is [abc]
2021-04-12 17:08:23.411005+0800 RACDemo[19726:287122] merge x is [ABC]
combineLatest
作用:按顺序合并多个信号的最新值为元组
例:
//创建信号
RACSubject *subject = [RACSubject subject];
//创建信号
RACSubject *subject2 = [RACSubject subject];
RACSignal *signal = [RACSignal combineLatest: @[subject, subject2]];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"combineLatest x is [%@]",x);
}];
//发送信号
[subject sendNext:@"a"];
//发送信号
[subject2 sendNext:@"b"];
//发送信号
[subject2 sendNext:@"A"];
//发送信号
[subject sendNext:@"B"];
返回结果如下:
2021-04-12 17:27:35.030304+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003af40b0> (
a,
b
)]
2021-04-12 17:27:35.030604+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003ae4ee0> (
a,
A
)]
2021-04-12 17:27:35.030805+0800 RACDemo[20891:303336] combineLatest x is [<RACTuple: 0x600003ae85c0> (
B,
A
)]
映射
flattenMap
作用: 用于信号中信号,把源信号的内容映射成一个新的信号,信号是RACSignal类型
- (RACSignal *)flattenMap:(__kindof RACSignal * _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
例:映射值后,返回一下新的信号
// //创建信号
RACSubject *subject = [RACSubject subject];
// 绑定信号,并返回映射后的值
RACSignal * flattenMapSignal =[subject flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
value=[NSString stringWithFormat:@"flattenMapSignal映射后的值为:%@",value];
return [RACReturnSignal return:value];
}];
//订阅信号
[flattenMapSignal subscribeNext:^(id _Nullable x) {
NSLog(@"flattenMapSignal x is [%@]",x);
}];
//发送信号
[subject sendNext:@"1234"];
返回结果如下:
2021-04-11 08:30:20.425283+0800 RACDemo[5943:81326] flattenMapSignal x is [flattenMapSignal映射后的值为:1234]
map
作用: 把源信号的值映射成一个新的值,可以是任意类型的
- (RACSignal *)map:(id _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
例:返回加工后的字符串
//创建信号
RACSubject *subject = [RACSubject subject];
//绑定信号
RACSignal *bindSignal = [subject map:^id _Nullable(id _Nullable value) {
//信号发送的内容
NSString *str = [NSString stringWithFormat:@"映射 map 处理后的数据:%@", value];
//返回任意类型的值
return str;
}];
//订阅信号
[bindSignal subscribeNext:^(id x) {
NSLog(@"map x is [%@]",x);
}];
//发送数据
[subject sendNext:@1];
返回结果如下:
2021-04-11 08:34:34.676217+0800 RACDemo[6105:85742] map x is [映射 map 处理后的数据:1]
例:返回一个新的信号
RACSubject *subject2=[RACSubject subject];
//绑定信号
RACSignal * bindSignal2=[subject2 map:^RACSignal * _Nullable(id _Nullable value) {
//映射后,返回一个新的信号对象
value=[NSString stringWithFormat:@"修改value %@",value];
return [RACReturnSignal return:value];
}];
//订阅信号
[bindSignal2 subscribeNext:^(id _Nullable x) {
NSLog(@"返回值是信号 [%@]",x);
}];
//发送信号
[subject2 sendNext:@(2)];
返回结果如下:
2021-04-11 08:41:03.927420+0800 RACDemo[6274:90310] 返回值是信号 [<RACReturnSignal: 0x600001e9d400> name: ]
过滤
filter
作用: 通过条件判断,过滤掉相应的信号
- (RACSignal<ValueType> *)filter:(BOOL (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;
例:过滤掉ABC开头的字符串
//创建信号
RACSubject *subject=[RACSubject subject];
//过滤掉ABC开头的字符串
RACSignal *signal= [subject filter:^BOOL(id _Nullable value) {
NSString *str=value;
if ([str hasPrefix:@"ABC"]) {
return NO;
}else{
return YES;
}
}];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"过滤后的结果为 [%@]",x);
}];
//发送信号
[subject sendNext:@"abc123"];
//发送信号
[subject sendNext:@"ABC123"];
返回结果如下:
2021-04-11 10:44:03.423107+0800 RACDemo[10384:185744] 过滤后的结果为 [abc123]
ignore
作用: 过滤信号值,忽略掉值为xxx的信号,按值忽略
- (RACSignal<ValueType> *)ignore:(nullable ValueType)value RAC_WARN_UNUSED_RESULT;
例:忽略掉值为ABC123的信号
//创建信号
RACSubject* subject=[RACSubject subject];
//忽略掉值为ABC123的信号
RACSignal *signal=[subject ignore:@"ABC123"];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"ignore x is [%@]",x);
}];
//发送信号
[subject sendNext:@"ABC123"];
//发送信号
[subject sendNext:@"abc123"];
返回结果如下:
2021-04-11 10:37:35.490486+0800 RACDemo[10205:180228] ignore x is [abc123]
then
- (RACSignal *)then:(RACSignal * (^)(void))block RAC_WARN_UNUSED_RESULT;
作用:忽略掉前一个信号发送的值,必须等前一个信号发送sendCompleted表示执行完成后,才执行下一个信号,否则无效,按信号忽略
例:忽略掉subject 信号,然后再执行subject2信号
//创建信号
RACSubject *subject = [RACSubject subject];
//创建信号
RACSubject *subject2 = [RACSubject subject];
//忽略掉subject 信号
RACSignal *signal = [subject then:^RACSignal * _Nonnull{
return subject2;
}];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"then x is [%@]",x);
}];
//发送信号
[subject sendNext:@"a"];
// 必须添加sendCompleted ,表示subject 信号执行完成,否则无效
[subject sendCompleted];
//发送信号
[subject2 sendNext:@"b"];
//发送信号
[subject2 sendNext:@"A"];
返回结果如下:
2021-04-12 17:45:59.354229+0800 RACDemo[22106:321645] then x is [b]
2021-04-12 17:45:59.354343+0800 RACDemo[22106:321645] then x is [A]
take
作用: 按顺序获取第1次到n次信号
- (RACSignal<ValueType> *)take:(NSUInteger)count RAC_WARN_UNUSED_RESULT;
例:获取从第1~4次信号
//创建信号
RACSubject* subject=[RACSubject subject];
//获取从第1~4次信号,若传0则不取信号
RACSignal *signal=[subject take:4];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"take x is [%@]",x);
}];
//发送信号
[subject sendNext:@"ABC123"];
//发送信号
[subject sendNext:@"123ABC"];
//发送信号
[subject sendNext:@"abc123"];
//发送信号
[subject sendNext:@"123abc"];
//发送信号
[subject sendNext:@"123"];
//发送信号
[subject sendNext:@"abc"];
返回结果如下:
2021-04-11 10:59:05.655107+0800 RACDemo[10795:197326] take x is [ABC123]
2021-04-11 10:59:05.655298+0800 RACDemo[10795:197326] take x is [123ABC]
2021-04-11 10:59:05.655436+0800 RACDemo[10795:197326] take x is [abc123]
2021-04-11 10:59:05.655573+0800 RACDemo[10795:197326] take x is [123abc]
skip
作用: 跳过从第1次~n次 的信号
例:跳过从第1~3次信号
//创建信号
RACSubject* subject=[RACSubject subject];
//跳过从第1~3次信号
RACSignal *signal=[subject skip:3];
//订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"skip x is [%@]",x);
}];
//发送信号
[subject sendNext:@"123"];
//发送信号
[subject sendNext:@"abc"];
//发送信号
[subject sendNext:@"ABC"];
//发送信号
[subject sendNext:@"123abc"];
返回结果如下:
2021-04-11 11:18:20.866481+0800 RACDemo[11294:209783] skip x is [123abc]
switchToLatest
作用: 信号内的信号,获取信号内信号发送的最新的信号,当发送多个内部信号时候,只接受最新的内部信号
例:
//创建信号
RACSubject* subject=[RACSubject subject];
//创建内部信号,用于subject内部的信号
RACSubject * innerSubject=[RACSubject subject];
//创建内部信号1,用于subject内部的信号
RACSubject * innerSubject1=[RACSubject subject];
//订阅信号,订阅subject内的即innerSubject信号
[subject.switchToLatest subscribeNext:^(id _Nullable x) {
NSLog(@"switchToLatest x is [%@]",x);
}];
//subject发送innerSubject信号
[subject sendNext:innerSubject];
//subject发送innerSubject1信号
[subject sendNext:innerSubject1];
//通过innerSubject来发送信号
[innerSubject sendNext:@"abc"];
//通过innerSubject1来发送信号
[innerSubject1 sendNext:@"123abc"];
返回结果如下:
2021-04-11 11:41:27.553097+0800 RACDemo[12001:229779] switchToLatest x is [123abc]
distinctUntilChanged
作用: 忽略重复的信号
例:执行后,忽略了重复的1
//创建信号
RACSubject* subject=[RACSubject subject];
//订阅信号,只在订阅的信号与上一次不同时才会响应
[subject.distinctUntilChanged subscribeNext:^(id _Nullable x) {
NSLog(@"distinctUntilChanged x is [%@]",x);
}];
//发送信号
[subject sendNext:@"1"];
//发送信号
[subject sendNext:@"1"];
//发送信号
[subject sendNext:@"123"];
返回结果如下:
2021-04-11 11:53:14.833259+0800 RACDemo[12347:240090] distinctUntilChanged x is [1]
2021-04-11 11:53:14.833524+0800 RACDemo[12347:240090] distinctUntilChanged x is [123]
总结
RAC的高内聚,低耦合特性,很好的配合了MVVM的使用。因此如果现在正在使用MVVM模式开发,不妨试一下RAC框架。 以上是对RAC中的常用类、函数、宏的一些使用说明,并未对每个功能的底层原理进行分析,主要是介绍RAC是如何应用于业务中的。学习一项新的框架的步骤,一般是先知其所用,而后再知其所以然,后续再对某些功能进行详细的分析。
网友评论