RAC 即 ReactiveCocoa,是 Github 开源的函数响应式编程框架。可以简化代码,使一个功能的代码集中到一起,提高了代码聚合性。
- 创建信号(冷信号)
- 订阅信号(热信号)
- 发送信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { NSLog(@"创建了信号");
[subscriber sendNext:@"ZM"]; NSLog(@"发送了信号");
return [RACDisposable disposableWithBlock:^{ }]; }];
[signal subscribeNext:^(id _Nullable x) { NSLog(@"订阅了信号"); NSLog(@"%@", x);
}];
|
应用场景
- 代替代理(更少代码)
rac_signalForSelector:
- 代替 KVO
rac_valuesAndChangesForKeyPath:
- 监听事件(用于 MVVM)
rac_signalForControllerEvents:
- 代替通知(自动 remove)
rac_addObserverForName:
- 监听文本框内容改变
rac_textSignal:
- 处理当界面有多次请求时,需要都获取到数据时,才能展示界面
rac_liftSelector:WithSignalFromArray:Signals:
常用方法
map & flattenMap
这是两个映射方法,将源信号内容映射成新的内容,就像 signal 管道上的中间处理器,从这里走过的 signal 都会经过一段处理后,变成新的 signal 继续传输。
map RACSignal *mapSignal = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; return nil; }] map:^id _Nullable(id _Nullable value) { return [NSString stringWithFormat:@"%@", value]; }]; [mapSignal subscribeNext:^(id _Nullable x) { NSLog(@"x"); }];
|
flattenMap RACSignal *mapSignal = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; return nil; }] flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) { return [RACReturnSignal return:[NSString stringWithFormat:@"%@", value]]; }]; [mapSignal subscribeNext:^(id _Nullable x) { NSLog(@"x"); }];
|
两者之间的区别就在于,map 中 Block 返回转换对象,flattenMap 返回转换对象的信号。一般信号发出的值不是信号,使用 map;如果是信号则使用 flattenMap,它可以处理信号中的信号。
merge
把多个信号合并成一个信号,只需订阅这一个信号就相当于订阅了多个信号,任何一个信号有新值的时候都会触发调用。
mergeRACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; return nil; }]; RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(2)]; return nil; }]; RACSignal *signal3 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(3)]; return nil; }]; RACSignal *mergeSignal = [RACSignal merge:@[signal1, signal2, signal3]]; [mergeSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
concat
将多个信号有顺序的连接起来,按照顺序接收信号,但是一定要之前的信号完成了才能发送下一个信号。
concatRACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendCompleted]; return nil; }]; RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(2)]; [subscriber sendCompleted]; return nil; }]; RACSignal *signal3 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(3)]; [subscriber sendCompleted]; return nil; }]; RACSignal *concatSignal = [RACSignal concat:@[signal2, signal1, signal3]]; [concatSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
then
用于连接两个信号,内部也是使用 concat,当前一个信号完成之后才会连接 then 返回的信号,但是会忽略前一个信号,只会触发下个信号。
thenRACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendCompleted]; return nil; }]; RACSignal *thenSignal = [signal1 then:^RACSignal * _Nonnull{ return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(2)]; [subscriber sendCompleted]; return nil; }]; }]; [thenSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
zip
将对各信号压缩成一个信号,只有当几个信号同时 sendNext 的时候才会触发压缩流的 next 事件,其中每一个信号 send 的内容都是一一对应的。
zipRACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendNext:@(3)]; return nil; }]; RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(2)]; return nil; }] ; RACSignal *zipSignal = [RACSignal zip:@[signal1, signal2]]; [zipSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
combineLatest
将多个信号组合起来,当其中每一个信号都 sendNext 之后,才会触发组合的信号,其中每一个信号再次 sendNext 都会覆盖之前的信号内容,返回的是一个 RACTuple(元组,类似于NSArray)。
combineLatestRACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendNext:@(3)]; return nil; }]; RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(2)]; return nil; }];
RACSignal *combineSignal = [RACSignal combineLatest:@[signal1, signal2]]; [combineSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
rac_liftSelector: withSignalsFromArray:
当信号组中每一个信号都至少一次 sendNext 之后,将触发 Selector 方法,类似于 combineLatest。
rac_liftSelector: withSignalsFromArray:RACSubject *subject1 = [RACSubject subject]; RACSubject *subject2 = [RACSubject subject]; [[self rac_liftSelector:@selector(updateWithParameter1:parameter2:) withSignals:subject1, subject2, nil] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [subject1 sendNext:@1]; [subject2 sendNext:@2];
|
reduceEach
一般用于元组,把元组的值聚合成一个值。
reduceEachRACSignal *combineSignal = [RACSignal combineLatest:@[signal1, signal2]]; RACSignal *reduceSignal = [combineSignal reduceEach:^id (NSNumber *num1, NSNumber *num2){ return @(num1.doubleValue+num2.doubleValue); }]; [reduceSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
filter
过滤信号,添加筛选条件,只有符合的才会触发调用。
filterRACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendNext:@"3"]; return nil; }]; RACSignal *filterSignal = [signal1 filter:^BOOL(id _Nullable value) { return [value isKindOfClass:[NSNumber class]]; }]; [filterSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
RACSequence *sequence = [@[@(1),@(2),@"3"].rac_sequence filter:^BOOL(id _Nullable value) { return [value isKindOfClass:[NSNumber class]]; }]; [sequence.signal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
distinctUntilChanged
当前值跟上一次的值不同的时候,就会触发调用,否则被忽略。
distinctUntilChanged[textField.rac_textSignal.distinctUntilChanged subscribeNext:^(NSString * _Nullable x) { NSLog(@"%@", x); }];
|
take
从第一个信号开始设置信号发送的有效的个数。
takeRACSignal *signal = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendNext:@(2)]; return nil; }] take:1];
[signal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
takeLast
从最后一个开始设置信号发送的有效个数,必须 sendCompleted,不然不知道总共多少个信号。
takeLast[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendNext:@(2)]; [subscriber sendCompleted]; return nil; }] takeLast:1] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
takeUntil
[signal1 takeUntil:signal2]
,当 signal2 已经 sendNext 或者 sendCompleted,signal1 就会失效。
takeUntilRACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; return nil; }]; RACSignal *signal2 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(2)]; return nil; }]; [[signal1 takeUntil:signal2] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
skip
跳跃,从第一个发出的信号开始跳。
skip[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; [subscriber sendNext:@(2)]; [subscriber sendNext:@(3)]; return nil; }] skip:2] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
doNext
在执行 sendNext 之前会执行这个。
doNext[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; return nil; }] doNext:^(id _Nullable x) { x = [NSString stringWithFormat:@"%@haha",x]; NSLog(@"%@", x); }] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
timeout
在超过设定时间范围之后让信号报错,且不能发送内容。
timeoutRACSubject *subject = [RACSubject subject]; [[subject timeout:3 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); } error:^(NSError * _Nullable error) { NSLog(@"%@", error); }]; [subject sendNext:@1]; dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(4 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ [subject sendNext:@2]; });
|
interval
定时,每隔一定时间发出时间信号。
interval [[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(NSDate * _Nullable x) { NSLog(@"%@", x); }];
|
delay
延时发送信号。
delay[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(1)]; return nil; }] delay:3] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
retry
重试,只要失败,就会重新执行创建信号中的 block,直到成功。
retry__block NSInteger i = 0; [[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { i++; if (i > 10) { [subscriber sendNext:@(1)]; }else{ [subscriber sendError:nil]; } return nil; }] retry] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); } error:^(NSError * _Nullable error) { NSLog(@"%@", error); }];
|
throttle
节流,当某个信号发送比较频繁的时候,可以限制在一定之间内不接受信号,等过了这个时间再取最后发送的信号内容发出,类似于 bufferWithTime:onScheduler:
。
throttleRACSubject *subject = [RACSubject subject];
[[subject throttle:1] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [subject sendNext:@1]; [subject sendNext:@2]; [subject sendNext:@3];
|
deliverOn
内容传递切换到指定线程中,副作用在原来线程中,把在创建信号时 block 中的代码称之为副作用。
subscribeOn
内容传递和副作用都会切换到指定线程中。
常用类
RACSubject
信号提供者,本身可以充当信号,又能发送信号,继承自 RACSignal,但是底层实现跟 RACSignal 有些不一样,当订阅信号的时候会创建订阅者并保存订阅响应 Block,而发送信号的时候会遍历订阅者,然后分别调用 nextBlock。它提供的 API 很少,但是经常使用,因为它继承自 RACSignal。这里顺便来看一下方法 flatten 跟 switchToLatest,这两个都只能用来处理信号中的信号。
flatten
压平信号中的信号,信号中的信号我们称之为子信号,flatten 可以拿到所有子信号发送的值。
flattenRACSubject *subject = [RACSubject subject]; RACSubject *subSubject1 = [RACSubject subject]; RACSubject *subSubject2 = [RACSubject subject]; [subject subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [subject.flatten subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [subject sendNext:subSubject1]; [subject sendNext:subSubject2]; [subSubject1 sendNext:@1]; [subSubject2 sendNext:@2];
|
switchToLatest
与 flatten 相同,压平信号中的信号,不同的是,在存在多个子信号时候只会拿到最新的子信号,然后输出最新的子信号的值。
switchToLatestRACSubject *subject = [RACSubject subject]; RACSubject *subSubject1 = [RACSubject subject]; RACSubject *subSubject2 = [RACSubject subject]; [subject subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [subject.switchToLatest subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [subject sendNext:subSubject1]; [subject sendNext:subSubject2]; [subSubject1 sendNext:@1]; [subSubject2 sendNext:@2];
|
RACReplaySubject
重复提供信号类,继承自 RACSubject,它可以先发送信号,再订阅信号,原理就是将发送的信号内容保存了起来,当订阅信号的时候再将之前保存的信号,由订阅者一个一个的发送出来,而保存信号的容量由 capacity 来控制。
RACReplaySubjectRACReplaySubject *replaySubject = [RACReplaySubject replaySubjectWithCapacity:5]; [replaySubject subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [replaySubject sendNext:@1]; [replaySubject subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
RACMulticastConnection
这是一个组播连接类,是对信号的一个封装处理,当一个信号被多次订阅时,则会多次执行 didSubscribe 这个 Block,造成副作用,而这个类就能避免多次执行 didSubscribe,是一对多的单向数据流,一般用来处理信号被多次订阅的情况。
RACMulticastConnection__block int i = 0;
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@(i)]; i ++; return nil; }];
RACMulticastConnection *connect = [signal publish] [connect.signal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; [connect.signal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
[connect connect];
|
方法解析及实现原理:
- publish,multicast:这是对RACMulticastConnection初始化方法的一个封装,publish其实就是调用了multicast,并把创建好的RACSubject对象传给它,而multicast也就是调用了RACMulticastConnection的初始化方法,将原始信号传给source,把RACSubject对象传给subject。
- 当我们订阅connect.signal,其实就是订阅subject,然后将subject的订阅者保存起来,而调用[connect connect]的时候,会订阅原始信号(source),而source的订阅者就是subject,这时候subject就会执行[subject sendNext],之后就会遍历subject所有的订阅者,逐一发送信号,触发外部subscribeNext回调。
RACCommand
这是一个命令类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程,一般来说是在UI上的某些动作来触发这些事件,比如点击一个按钮,RACCommand的实例能够决定是否可以被执行,一般用于网络请求,监控请求过程。
- 创建命令,初始化RACCommand对象
- 在signalBlock中创建RACSignal,用来做数据传递,如果不需要可以创建空信号[RACSignal empty]。
- 执行命令execute
RACCommand RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { NSLog(@"%@", input); return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@(2)]; [subscriber sendCompleted]; return nil; }]; }];
[command.executionSignals.switchToLatest subscribeNext:^(id x) { NSLog(@"%@", x); }];
[[command.executionSignals flatten] subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
[[command.executing skip:1] subscribeNext:^(id x) { if ([x boolValue] == YES) { NSLog(@"正在执行"); }else{ NSLog(@"执行完成"); } }];
RACSignal *connectSignal = [command execute:@1] ; [connectSignal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
RACChannel
这是一个通道类,可以理解为一个双向的连接,连接的两端都配有RACChannelTerminal(通道终端,继承自RACSignal,且又实现了RACSubscriber协议,所以它可以充当信号,又能发送信号),分别是leadingTerminal,followingTerminal,只要其中任何一端输出信号,另一端都会有相同的信号输出。我们平时很少直接使用RACChannel,而是使用RACChannelTo。
RACChannelRACChannelTerminal *followT = RACChannelTo(view, backgroundColor); [followT subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }]; UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init]; [tap.rac_gestureSignal subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) { [followT sendNext:RandomColor]; }];
RACChannelTo(button, backgroundColor) = RACChannelTo(view, backgroundColor);
|
常用宏
RAC(TARGET, ...)
给某个对象的某个属性进行绑定。
RAC(TARGET, ...) RAC(_button, enabled) = [[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] mapReplace:@NO];
|
RACObserve(TARGET, KEYPATH)
KVO,监听某个对象的属性,返回的是信号。
RACObserve(TARGET, KEYPATH)[RACObserve(self.view, backgroundColor) subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
RACChannelTo
用于双向绑定的一个通道终端。
RACChannelTo RACChannelTo(button, backgroundColor) = RACChannelTo(view, backgroundColor);
|
RACTuplePack
将数据包装成 RACTuple(元组)。
RACTuplePackRACTuple *tuple = RACTuplePack(@1, @2, @"3");
|
RACTupleUnpack(...)
把元组解包成对应的数据
RACTupleUnpack(...) RACTupleUnpack(NSNumber *num1, NSNumber *num2) = tuple;
NSLog(@"%@, %@", num1, num2);
|