RP
概念
RP(Reactive Programming)——响应编程,实际上是异步事件流(Asynchronous event stream)。
根本
Stream
stream 流。一切都可以看成流,变量、用户输入、属性、事件、cache、数据结构。那么我们基于流,可以进行各种操作,combine,creat,filter,等等。对于流处理得到的数据又可以看做是新的流,以此进行不停的操作。
而对于一个流,包括三部分,即三个 events:value,error,complete。
当我们异步去捕获这些事件时,即为异步事件流。
如何捕获?我们采用监听的方式,去监听 stream,也成为订阅。定义的角色就是观察者,被监听的 stream 则是被观察者,其实也就是所谓的观察者模式。
对 stream 操作
首先对于常见的 RP 库,对于 stream 都有很多方法,map
、filter
、scan
等等,当时他们有一个共同的特点,就是返回值是一个新的 stream。
注意:
上文说的是产生新的stream,也就是说对初始的那个 stream 并没有做出修改,这个特性,就是不可变性(immutability)。
新的 stream 可以继续进行相同的函数操作,这就是所谓的函数式编程。
一个小例子
我们想在一连串的点击中检测例如鼠标点击那样的双击(包括三连击或者更高),间隔为250ms。
我首先想到的是获取点击时间点放入数组,遍历数组获取时间间隔放入数组,再遍历获取超过250ms的时间点的 index。再对应初始数组的位置取连击位置。略显复杂,且用到多个数组和遍历操作。
如果用流的方式的话...
使用
throttle
来设定有效时间,使用map
来进行映射,使用filter
进行筛选,最后生成的 stream 即是我们需要的新的 stream。我们可以通过订阅(监听)的方式来做出响应。
优美的逻辑和代码方式。
FRP
概念
函数响应式编程(英文Functional Reactive Programming),简称FRP。
特点
int a = 3;
int b = 4;
int c = a + b;
NSLog(@"c is %d", c); // c is 7
a = 5;
b = 7;
NSLog(@"c is %d", c); // 仍然是 c is 7
上面就是典型的命令式编程,通过表达式和语句来改变状态量,命令式编程int c = a + b;
只是一个瞬间的过程,而并不是关系描述。在传统开发中,使c的值随a、b变化而变化是比较难的。
而响应式编程的出现就是为了描述这种实时变化的情况,也是响应式编程的核心思想。
// 代替target-action
[[self.confirmButton rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(id x) {
// 回调内容写在这里
}];
// 代替delegate
[[self.scrollView rac_signalForSelector:@selector(scrollViewDidScroll:) fromProtocol:@protocol(UIScrollViewDelegate)]
subscribeNext:^(id x) {
// 回调内容写在这里
}];
// 代替block
[[self asyncProcess]
subscribeNext:^(id x) {
// 回调内容写在这里
} error:^(NSError *error) {
// 错误处理写到这里
}];
// 代替notification center
[[[NSNotificationCenter defaultCenter] rac_valuesForKeyPath:@"Some-key" observer:nil]
subscribeNext:^(id x) {
// 回调内容写在这里
}];
// 代替KVO
[RACObserve(self, userReportString)
subscribeNext:^(id x) {
// 回调内容写在这里
}];
导入
OC版本:pod 'ReactiveObjC'
Swift版本:pod ‘ReactiveCocoa’
需要在podfile加上use_frameworks,重新pod install 才能导入成功
RAC接口
RACSignal
介绍:
信号,RP 中流概念(stream)在 RAC 中的对应,是贯穿 RAC 的核心概念,继承自 RACStream。
构造方法:
- 创建一个普通信号
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;
/// 冷信号
RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
[subscriber sendNext:@"foobar"];
[subscriber sendCompleted];
return nil;
}];
- 创建一个立即完成的信号
// Returns a signal that immediately completes.
+ (RACSignal<ValueType> *)empty RAC_WARN_UNUSED_RESULT;
普通方法:
- 订阅
//订阅后的冷信号变为热信号,改变时触发block中内容
[signal subscribeCompleted:^{
NSLog(@"subscription %u", subscriptions);
}];
- bool值无变化过滤
- (RACSignal<ValueType> *)distinctUntilChanged RAC_WARN_UNUSED_RESULT;
//区别于searchText每次变化同时validSearchSignal被赋值
//加入distinctUntilChanged标记只有block内条件变化时validSearchSignal才被赋值。
RACSignal *validSearchSignal =
[[RACObserve(self, searchText)
map:^id(NSString *text) {
return @(text.length > 3);
}]
distinctUntilChanged];
- 延迟
- (RACSignal<ValueType> *)delay:(NSTimeInterval)interval RAC_WARN_UNUSED_RESULT;
- 打印
/// Logs all events that the receiver sends.
- (RACSignal<ValueType> *)logAll RAC_WARN_UNUSED_RESULT;
RACCommand
介绍:
A command is a signal triggered in response to some action, typically UI-related.
UI Action 类型的一种信号
构造方法:
- (instancetype)initWithSignalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
- (instancetype)initWithEnabled:(nullable RACSignal<NSNumber *> *)enabledSignal signalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
enabled 决定该事件是否可用。
block 决定点击事件处理,block 中 Input 为点击事件传参,类似于 sender,返回的 RACSignal 为事件处理结果信号。
属性:
- executing
A signal of whether this command is currently executing.
//按钮点下搜索同时改变app状态栏中网络activity indicator
RAC([UIApplication sharedApplication], networkActivityIndicatorVisible) = self.viewModel.executeSearch.executing;
网友评论