RAC初探
前言
RAC,全称是,RAC就是一个简化代码的第三方库;是函数式编程和响应式编程的结合。函数式编程的第一个特点就是可以把函数作为参数传递给另一个函数,第二个特点就是可以返回一个函数,这样就可以实现。响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
在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. 实现的功能
@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. 实现的功能
一般需要监听键盘弹出通知的做法是先注册通知的接收,然后在通过@对接收到的信息进行处理。而使用rac通过链式可以在一个方法里实现,简单明了。
- (void)racNotifyTest {[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {NSLog(@"=====x就是通知的结果====");}];
}
其实还有手势的响应、按钮的点击事件等等可以用@响应的事件都可以使用RAC实现。
将集合类型的数据封装成序列,然后通过对序列的统一操作,进行的处理,比如说遍历等。
NSArray *arr = @[@"AA", @"BB", @"CC", @"DD"];
[arr.rac_sequence.signal subscribeNext:^(id _Nullable x) {NSLog(@"==相当于遍历==%@==", x);
}];
RAC高阶用法
1. 信号映射:map与
[[self.textField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {// 进行相关的映射操作return [RACReturnSignal return:@""];
}] subscribeNext:^(id _Nullable x) {}];
2.信号过滤:、、
[[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.信号合并: 、、merge、
信号合并,任何一个信号发送数据,都能监听到。
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.信号连接:、then
[[[RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {[subscriber sendNext:@"AAA"];[subscriber sendCompleted];return nil;
}] then:^RACSignal * _Nonnull{return [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {[subscriber sendNext:@"BBB"];[subscriber sendCompleted];return nil;}];}] subscribeNext:^(id _Nullable x) {NSLog(@"==%@==",x);
}];
5.信号操作时间:、、dely
延迟处理
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id 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、、
// 只监听前面几次取值 这里就是只监听前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: block并没有返回值。因为它是附加操作,并不改变事件本身。
[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] doNext:^(id x) {self.button.enabled = NO;
}] subscribeNext:^(__kindof UIControl * _Nullable x) {NSLog(@"====%@===", x);
}];
9.获取信号中的信号:
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 subscriber) {if (i == 10) {[subscriber sendNext:@1];}else{NSLog(@"接收到错误");[subscriber sendError:nil];}i++;return nil;
}] retry] subscribeNext:^(id x) {NSLog(@"%@",x);} error:^(NSError *error) {}];
信号事件响应的分析
通过上述的例子,我们知道当监听的信号时,就可以在的block事件里面进行对相关变化的处理,那么是如何做到的呢?我们来跟踪一下代码:
[self.tf.rac_textSignal subscribeNext:^(NSString * _Nullable x) {NSLog(@"==%@==", x);
}];
首先,我们调用的属性时,就会进入如下方法:
@implementation UITextField (RACSignalSupport)- (RACSignal *)rac_textSignal {@weakify(self);return [[[[[RACSignaldefer:^{@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 :]就是对编辑事件响应的操作处理。我们知道继承自,其事件响应就来自于,而该方法则是对的扩展。
@implementation UIControl (RACSignalSupport)- (RACSignal *)rac_signalForControlEvents:(UIControlEvents)controlEvents {@weakify(self);return [[RACSignalcreateSignal:^(id 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];
}
提前拦截的响应事件,而通过来实现其响应事件,这样就可以响应的编辑事件了。
RAC流程 RAC信号生命周期
- (void)signalTest {RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id _Nonnull subscriber) {[subscriber sendNext:@"发送信号"];return [RACDisposable disposableWithBlock:^{NSLog(@"销毁信号");}];}];[signal subscribeNext:^(id _Nullable x) {NSLog(@"==订阅信号==%@==", x);}];
}
可以看出先执行了,然后才会执行。
我们来跟踪一下代码的流程:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {return [RACDynamicSignal createSignal:didSubscribe];
}+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe {RACDynamicSignal *signal = [[self alloc] init];// 保存blocksignal->_didSubscribe = [didSubscribe copy];return [signal setNameWithFormat:@"+createSignal:"];
}
创建信号的时候,就会把这个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后面的blocksubscriber->_next = [next copy];subscriber->_error = [error copy];subscriber->_completed = [completed copy];return subscriber;
}- (RACDisposable *)subscribe:(id)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:^{// 调用保存的blockRACDisposable *innerDisposable = self.didSubscribe(subscriber);[disposable addDisposable:innerDisposable];}];[disposable addDisposable:schedulingDisposable];}return disposable;
}
调用方法的时候,首先会保存后面block的内容,然后会调用之前保存的这个block,接着就会调用block中的方法。
- (void)sendNext:(id)value {@synchronized (self) {void (^nextBlock)(id) = [self.next copy];if (nextBlock == nil) return;// 调用subscribeNext后面的blocknextBlock(value);}
}
该方法会把后面的参数传给之前保存的的block,然后调用,此时控制台就会输出:
==订阅信号==发送信号==
然后才会调用中的方法,即销毁信号。整个流程整理如下:
总结 RAC的特点是函数式+响应式RAC对很多事件都进行了封装,更加的便于使用。比如KVO、代理等RAC对事件的响应是基于拦截的方法:
- (void)addTarget:(nullable id)target action:(SEL)action forControlEvents:(UIControlEvents)controlEvents
RAC的流程: