ReactiveCocoa操作
所有的信号(RACSignal)都可以进行操作处理,因为所有的操作方法都定义在RACStream.h中,而RACSignal继承RACStream。
ReactiveCocoa操作思想
运用的是(钩子)HooK思想,Hook是一种用于改变API执行结果的技术。
Hook用处:截获API调用的技术。
Hook原理:在每次调用一个API返回结果之前,先执行自己的方法,改变方法的输出。
RAC开发方式:RAC核心开发方式也就是绑定,之前的开发方式是赋值,而RAC开发应该把重心放在绑定,也就是在创建一个对象的时候就绑定好以后想要做的事情,而不是等赋值之后在去做事情。
例如:把数据展示在控件上,之前都是重写控件的setModel方法,用RAC就可以在一开始创建控件的时候就绑定好数据
ReactiveCocoa核心方法bind
ReactiveCocoa操作的核心方法是bind(绑定),给RAC中的信号进行绑定,只要信号一发送数据就能监听到,从而把发送数据改成自己想要的数据。
在开发中很少使用bind方法,bind属于RAC中的底层方法,RAC已经封装了很多好用的其他方法,底层都是调用bind,用法比bind简单。
bind方法简单介绍和使用:
// 创建信号
- RACSubject *subject = [RACSubject subject];
// 绑定信号,只有绑定的信号被订阅就会被调用
// bind 返回一个绑定信号 - RACSignal *bindsignal = [subject bind:^RACStreamBindBlock{
return ^RACSignal *(id value, BOOL *stop){
// block调用:只要原信号发送数据,就会调用block
// block作用:处理原信号内容
// value:原信号发送的内容
NSLog(@"接收到原信号的内容%@",value);
// 返回信号,不能传nil,返回空信号[RACSignal empty]
value = @"程倩";
return [RACReturnSignal return:value];
};
}];
- [bindsignal subscribeNext:^(id x) {
NSLog(@"接收到绑定信号处理完的内容%@",x);
}];
// 原型号发送数据 - [subject sendNext:@"123"];
输出:
2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到原信号的内容123
2016-05-17 11:41:00.549 ReactiveCocoaTest1[1102:67222] 接收到绑定信号处理完的内容程倩
分析
-
RACSubject *subject = [RACSubject subject];
创建一个RACSubject原信号 初始化一个数组_subscribers
源码:
@implementation RACSubject#pragma mark Lifecycle
- (instancetype)subject {
return [[self alloc] init];
}
-
(id)init {
self = [super init];
if (self == nil) return nil;
//初始化一个订阅者和_subscribers数组
_disposable = [RACCompoundDisposable compoundDisposable];
_subscribers = [[NSMutableArray alloc] initWithCapacity:1];return self;
}
- (instancetype)subject {
RACSignal *bindsignal = [subject bind:^RACStreamBindBlock{
return ^RACSignal (id value, BOOL stop){
NSLog(@"接收到原信号的内容%@",value);
// 返回信号,不能传nil,返回空信号[RACSignal empty]
value = @"程倩";
return [RACReturnSignal return:value];
};
}];
bind:这个函数存在于RACSubject的父类RACSignal中
源码:
/
- @param block 传入的block
- @return 返回绑定后的信号
*/
- (RACSignal *)bind:(RACStreamBindBlock (^)(void))block {
NSCParameterAssert(block != NULL);// 一个异常处理,不太懂
// 直接返回了一个绑定后的信号 创建一个新的信号返回,创建的是(RACDynamicSignal类型信号)
// RACDynamicSignal是RACSignal的子类 将这个didSubscribe block块保存在了RACDynamicSignal中的_didSubscribe block中
{
可以看到 [RACSignal createSignal:]中返回的结果是[RACDynamicSignal createSignal:didSubscribe];
看一下[RACDynamicSignal createSignal:didSubscribe]中的源码:
@implementation RACDynamicSignal
- (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
返回的是RACDynamicSignal对象,并且将传入的block块保存在了对象的_didSubscribe中
}
// 所以说这个block现在并没有被执行,只是将这个block存在了绑定信号中了
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
//当绑定的信号被订阅的时候开始执行这个block
//调用block后得到创建绑定信号时传入block返回的block 这个block返回一个新的信号
RACStreamBindBlock bindingBlock = block();
NSMutableArray *signals = [NSMutableArray arrayWithObject:self];
RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
// 这个block暂时不会被执行
void (^completeSignal)(RACSignal *, RACDisposable *) = ^(RACSignal *signal, RACDisposable *finishedDisposable) {
BOOL removeDisposable = NO;
@synchronized (signals) {
[signals removeObject:signal];
if (signals.count == 0) {
[subscriber sendCompleted];
[compoundDisposable dispose];
} else {
removeDisposable = YES;
}
}
if (removeDisposable) [compoundDisposable removeDisposable:finishedDisposable];
};
// 这个block暂时不会被执行
void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
@synchronized (signals) {
[signals addObject:signal];
}
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
// 在这里对返回的RACReturnSignal内容处理完成的信号进行订阅
RACDisposable *disposable = [signal subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(signal, selfDisposable);
}
}];
selfDisposable.disposable = disposable;
};
@autoreleasepool {
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
//当代码执行到这里的时候
// 在这里对原信号进行订阅,当原信号发送数据的时候会调用这个Nextblock
RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
if (compoundDisposable.disposed) return;
BOOL stop = NO;
// 当原信号发送数据的时候在这里调用了之前绑定block返回的block内容,得到一个信号
id signal = bindingBlock(x, &stop);
@autoreleasepool {
// 这这里执行了addSignal block,传入绑定block执行完毕返回的信号
if (signal != nil) addSignal(signal);
if (signal == nil || stop) {
[selfDisposable dispose];
completeSignal(self, selfDisposable);
}
}
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(self, selfDisposable);
}
}];
selfDisposable.disposable = bindingDisposable;
}
return compoundDisposable;
}] setNameWithFormat:@"[%@] -bind:", self.name];
}
3.订阅绑定信号:
[bindsignal subscribeNext:^(id x) {
NSLog(@"接收到绑定信号处理完的内容%@",x);
}];
分析:
subscribeNext:函数源码
bindsignal的类型是RACDynamicSignal类型
(RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
[self subscribe:o]的源码是:-
(RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
// 这个函数里面记录了绑定信号的订阅者
源码{
- (instancetype)initWithSubscriber:(id<RACSubscriber>)subscriber signal:(RACSignal *)signal disposable:(RACCompoundDisposable *)disposable {
NSCParameterAssert(subscriber != nil);self = [super init];
if (self == nil) return nil;// 记录下了绑定信号的订阅者
_innerSubscriber = subscriber;
_signal = signal;
_disposable = disposable;[self.innerSubscriber didSubscribeWithDisposable:self.disposable];
return self;
}
pragma mark RACSubscriber
-
(void)sendNext:(id)value {
if (self.disposable.disposed) return;if (RACSIGNAL_NEXT_ENABLED()) {
RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
}
// 在这里拿到绑定后的信号订阅者发送数据
[self.innerSubscriber sendNext:value];
}}
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
// 在这里可以看到订阅绑定信号的时候调用了创建绑定信号时候传入的block 跳转到2步骤中的 block
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
return disposable;
}
- // 原型号发送数据
[subject sendNext:@"123"];
分析:
拿到数组中的所有的block调用,(原信号被订阅时候存下的block)
例子:
假设想监听文本框的内容,并且在每次输出结果的时候,都在文本框的内容拼接一段文字“输出:”
// 方式一:在返回结果后,拼接。
[_textfield.rac_textSignal subscribeNext:^(id x) {
NSLog(@"输出:%@",x);
}];
// 方式二:在返回结果前,拼接,使用RAC中bind方法做处理。
// bind方法参数:需要传入一个返回值是RACStreamBindBlock的block参数
// RACStreamBindBlock是一个block的类型,返回值是信号,参数(value,stop),因此参数的block返回值也是一个block。
// RACStreamBindBlock:
// 参数一(value):表示接收到信号的原始值,还没做处理
// 参数二(*stop):用来控制绑定Block,如果*stop = yes,那么就会结束绑定。
// 返回值:信号,做好处理,在通过这个信号返回出去,一般使用RACReturnSignal,需要手动导入头文件RACReturnSignal.h。
// bind方法使用步骤:
// 1.传入一个返回值RACStreamBindBlock的block。
// 2.描述一个RACStreamBindBlock类型的bindBlock作为block的返回值。
// 3.描述一个返回结果的信号,作为bindBlock的返回值。
// 注意:在bindBlock中做信号结果的处理。
// 底层实现:
// 1.源信号调用bind,会重新创建一个绑定信号。
// 2.当绑定信号被订阅,就会调用绑定信号中的didSubscribe,生成一个bindingBlock。
// 3.当源信号有内容发出,就会把内容传递到bindingBlock处理,调用bindingBlock(value,stop)
// 4.调用bindingBlock(value,stop),会返回一个内容处理完成的信号(RACReturnSignal)。
// 5.订阅RACReturnSignal,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。
// 注意:不同订阅者,保存不同的nextBlock,看源码的时候,一定要看清楚订阅者是哪个。
// 这里需要手动导入#import <ReactiveCocoa/RACReturnSignal.h>,才能使用RACReturnSignal。
[[_textfield.rac_textSignal bind:^RACStreamBindBlock{
// 什么时候调用:
// block作用:表示绑定了一个信号.
return ^RACStream *(id value, BOOL *stop){
// 什么时候调用block:当信号有新的值发出,就会来到这个block。
// block作用:做返回值的处理
// 做好处理,通过信号返回出去.
return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]];
};
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
不正确之处,欢迎补充
测试代码 https://github.com/CharType/ReactiveCocoaTest