iOS-RAC初探
Posted MinggeQingchun
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了iOS-RAC初探相关的知识,希望对你有一定的参考价值。
前言
RAC
,全称是ReactiveCocoa
,RAC就是一个简化代码的第三方库ReactiveCocoa;是函数式编程和响应式编程的结合。函数式编程的第一个特点就是可以把函数作为参数传递给另一个函数,第二个特点就是可以返回一个函数,这样就可以实现。响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
在ios
中我们常见函数式编程就是block
,而响应式编程则是KVO
。这两种机制都对我们编程带了极大的便利性。而RAC
的出现会让我们的编程变更加的简洁明了。下面我们就来见识一下RAC
的强大之处。
RAC
的基础用法
1. 实现KVO
的功能
使用KVO
需要注册观察者、实现被观察对象改变的回调,不需要观察的时候还要移除观察者,而我们使用RAC
的时候只需要一行代码即可实现:
@property (nonatomic, copy) NSString *name;
- (void)racKVOTest {
[RACObserve(self, name) subscribeNext:^(id _Nullable x) {
NSLog(@"=====x就是改变====");
}];
}
2. 实现Delegate
的功能
@property (nonatomic, strong) UITextField *textField;
- (void)racDelegateTest {
self.textField.delegate = self;
[[self rac_signalForSelector:@selector(textFieldDidBeginEditing:) fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"=====相当于代理的实现====");
}];
[self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
NSLog(@"===监听textField的变化====%@===", x);
}];
}
3. 实现NotificationCenter
的功能
一般需要监听键盘弹出通知的做法是先注册通知的接收,然后在通过@selector
对接收到的信息进行处理。而使用rac
通过链式可以在一个方法里实现,简单明了。
- (void)racNotifyTest {
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"=====x就是通知的结果====");
}];
}
其实还有手势的响应、按钮的点击事件等等可以用@selector
响应的事件都可以使用RAC
实现。
4. 序列的相关操作
将集合类型的数据封装成序列sequence
,然后通过对序列的统一操作,进行signal
的处理,比如说遍历等。
NSArray *arr = @[@"AA", @"BB", @"CC", @"DD"];
[arr.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"==相当于遍历==%@==", x);
}];
RAC
高阶用法
1. 信号映射:map
与flattenMap
[[self.textField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
// 进行相关的映射操作
return [RACReturnSignal return:@""];
}] subscribeNext:^(id _Nullable x) {
}];
2.信号过滤:filter
、ignore
、distinctUntilChanged
[[self.textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
// 进行相关的过滤操作
return YES;
}] subscribeNext:^(NSString * _Nullable x) {
}];
// 忽略掉ignore的值
[[self.textField.rac_textSignal ignore:@"c"] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"====%@====",x);
}];
3.信号合并: combineLatest
、reduce
、merge
、zipWith
信号合并,任何一个信号发送数据,都能监听到。
RACSignal *signalA = self.textField.rac_textSignal;
RACSignal *signalB = [self.button rac_signalForControlEvents:UIControlEventTouchUpInside];
RACSignal *comSignal = [signalA combineLatestWith:signalB];
[comSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
RACSignal *signalA = self.textField.rac_textSignal;
RACSignal *signalB = [self.button rac_signalForControlEvents:UIControlEventTouchUpInside];
RACSignal *reduceSignal = [RACSignal combineLatest:@[signalA,signalB] reduce:^(id value1, id value2){
return [NSString stringWithFormat:@"reduce == %@ %@",value1,value2];
}];
[reduceSignal subscribeNext:^(id _Nullable x) {
NSLog(@"subscribeNext == %@",x);
}];
4.信号连接:concat
、then
[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"AAA"];
[subscriber sendCompleted];
return nil;
}] then:^RACSignal * _Nonnull{
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"BBB"];
[subscriber sendCompleted];
return nil;
}];
}] subscribeNext:^(id _Nullable x) {
NSLog(@"==%@==",x);
}];
5.信号操作时间:timeout
、interval
、dely
延迟处理
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
[subscriber sendNext:@"delay"];
[subscriber sendCompleted];
}];
return nil;
}] timeout:5 onScheduler:[RACScheduler mainThreadScheduler]];
[signal subscribeNext:^(id x) {
NSLog(@"%@", x);
} error:^(NSError *error) {
NSLog(@"%@", error);
}];
6.信号取值:take
、takeLast
、takeUntil
// 只监听前面几次取值 这里就是只监听前5次
[[self.textField.rac_textSignal take:5] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"==take===%@===", x);
}];
// 只监听最后几次取值 这里就是只监听最后5次
[[self.textField.rac_textSignal takeLast:5] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"==takeLast===%@===", x);
}];
7.信号跳过:skip
// 表示输入第一次,不会被监听到,跳过第一次发出的信号
[[self.textField.rac_textSignal skip:1] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
8.信号发送顺序:donext
、cocompleted
// 是直接跟在按钮点击事件的后面。
// 而且doNext: block并没有返回值。因为它是附加操作,并不改变事件本身。
[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] doNext:^(id x) {
self.button.enabled = NO;
}] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"====%@===", x);
}];
9.获取信号中的信号:switchToLatest
RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];
// 获取信号中信号最近发出信号,订阅最近发出的信号。
// 注意switchToLatest:只能用于信号中的信号
[signalOfSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[signalOfSignals sendNext:signal];
[signal sendNext:@1];
10.信号错误重试:retry
只要失败,就会重新执行创建信号中的block
,直到成功为止。
[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
if (i == 10) {
[subscriber sendNext:@1];
}else{
NSLog(@"接收到错误");
[subscriber sendError:nil];
}
i++;
return nil;
}] retry] subscribeNext:^(id x) {
NSLog(@"%@",x);
} error:^(NSError *error) {
}];
信号事件响应的分析
通过上述的例子,我们知道当监听UITextField
的信号时,就可以在subscribeNext
的block
事件里面进行对UITextField
相关变化的处理,那么rac_textSignal
是如何做到的呢?我们来跟踪一下代码:
[self.tf.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
NSLog(@"==%@==", x);
}];
首先,我们调用UITextField
的rac_textSignal
属性时,就会进入如下方法:
@implementation UITextField (RACSignalSupport)
- (RACSignal *)rac_textSignal {
@weakify(self);
return [[[[[RACSignal
defer:^{
@strongify(self);
// 返回信号
return [RACSignal return:self];
}]
concat:
// 拼接信号响应相关的操作 响应所有的编辑事件
[self rac_signalForControlEvents:UIControlEventAllEditingEvents]]
map:^(UITextField *x) {
// 对UITextField的过滤、映射处理
return x.text;
}]
takeUntil:
// 释放信号
self.rac_willDeallocSignal]
// 实现信号的Description方法
setNameWithFormat:@"%@ -rac_textSignal", RACDescription(self)];
}
可以看出[self rac_signalForControlEvents:UIControlEventAllEditingEvents]
就是对UITextField
编辑事件响应的操作处理。我们知道UITextField
继承自UIControl
,其事件响应就来自于UIControl
,而该方法则是对UIControl
的扩展。
@implementation UIControl (RACSignalSupport)
- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {
@weakify(self);
return [[RACSignal
createSignal:^(id<RACSubscriber> subscriber) {
@strongify(self);
// 拦截UITextField的事件,并将响应selector改为自己实现的`sendNext`
[self addTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
RACDisposable *disposable = [RACDisposable disposableWithBlock:^{
[subscriber sendCompleted];
}];
[self.rac_deallocDisposable addDisposable:disposable];
return [RACDisposable disposableWithBlock:^{
@strongify(self);
// 销毁信号 移除事件响应
[self.rac_deallocDisposable removeDisposable:disposable];
[self removeTarget:subscriber action:@selector(sendNext:) forControlEvents:controlEvents];
}];
}]
setNameWithFormat:@"%@ -rac_signalForControlEvents: %lx", RACDescription(self), (unsigned long)controlEvents];
}
提前拦截UITextField
的响应事件,而通过subscribeNext
来实现其响应事件,这样rac_textSignal
就可以响应UITextField
的编辑事件了。
RAC
流程
RAC
信号生命周期
- (void)signalTest {
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"发送信号"];
return [RACDisposable disposableWithBlock:^{
NSLog(@"销毁信号");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"==订阅信号==%@==", x);
}];
}
可以看出先执行了subscribeNext
,然后才会执行disposableWithBlock
。
我们来跟踪一下代码的流程:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
return [RACDynamicSignal createSignal:didSubscribe];
}
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
// 保存block
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
创建信号的时候,就会把didSubscribe
这个block
保存起来。
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
RACSubscriber *subscriber = [[self alloc] init];
// 保存subscribeNext后面的block
subscriber->_next = [next copy];
subscriber->_error = [error copy];
subscriber->_completed = [completed copy];
return subscriber;
}
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
// 调用保存的block
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
return disposable;
}
调用subscribeNext
方法的时候,首先会保存subscribeNext
后面block
的内容,然后会调用之前保存的didSubscribe
这个block
,接着就会调用block
中的sendNext
方法。
- (void)sendNext:(id)value {
@synchronized (self) {
void (^nextBlock)(id) = [self.next copy];
if (nextBlock == nil) return;
// 调用subscribeNext后面的block
nextBlock(value);
}
}
该方法会把sendNext
后面的参数传给之前保存的subscribeNext
的block
,然后调用,此时控制台就会输出:
==订阅信号==发送信号==
然后才会调用disposableWithBlock
中的方法,即销毁信号。整个流程整理如下:
总结
RAC
的特点是函数式+响应式
RAC
对很多事件都进行了封装,更加的便于使用。比如KVO
、代理等RAC
对事件的响应是基于拦截UIControl
的方法:
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
RAC
的流程:- 创建信号,保存
didSubscribe
- 订阅信号,保存
subscribeNext
- 调用
didSubscribe
,发送信号sendNext
- 执行
subscribeNext
- 创建信号,保存
以上是关于iOS-RAC初探的主要内容,如果未能解决你的问题,请参考以下文章