ReactiveCocoa的bind方法
RAC的Bind方法
RAC提供了一堆可以提高开发效率的方法,比如filter
,map
,flattenMap
等值处理方法,几乎每个方法点到底,都能看到一个叫做bind
的方法.这个方法就是RAC相对底层的方法.弄明白它,对于理解RAC是非常有帮助的.
现象
- (void)runTest {
// 1.
RACSubject *subject = [RACSubject subject];
// 2.
RACSignal *bindSignal = [subject bind:^RACSignalBindBlock _Nonnull {
return ^RACSignal *( id value, BOOL *stop ) {//这个block只要源信号发送数据,就会调用
NSLog(@"接收到源信号的内容: %@",value);
//2.1
//返回信号,不能传nil,如果真的想返回空信号,用empty(其实就是一个alloc
// init) . 如果返回empty,那就订阅链就断了.
// return [RACSignal empty];
return [RACReturnSignal return:value];
};
}];
// 3.
[bindSignal subscribeNext:^( id _Nullable x ){
//3.1
NSLog(@"订阅的地方,也就是处理完的信号:%@",x);
}];
// 4.
[subject sendNext:@"123"];
}
1.创建源信号
2.通过bind得到绑定信号
任何信号都能调用 bind 方法,bind方法需要一个RACSignalBindBlock
类型的参数,这个类型定义typedef RACSignal * _Nullable (^RACSignalBindBlock)(id _Nullable value, BOOL *stop)
, 早期版本,返回值是 RACStream
,现在是 RACSignal
,其实都一样.RACSignal
继承RACStream
. 响应式编程中,万物皆是流
3.订阅绑定信号
3.1 如果2.1处返回的是 empty
, 那么3.1处将不会执行.
4.源信号发送数据
从源码看
- 创建信号
RACSubject *subject = [RACSubject subject];
RACSubject.m文件中
- (instancetype)init {
self = [super init];
if (self == nil) return nil;
_disposable = [RACCompoundDisposable compoundDisposable];
_subscribers = [[NSMutableArray alloc] initWithCapacity:1];
return self;
}
_subscribers
这个是订阅者数组.
- 绑定信号
[subject bind:^RACSignalBindBlock _Nonnull{....}
这个bind点进去,里面代码如下:
- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block {
NSCParameterAssert(block != NULL);
//没有 block() ,即没有执行参数block
//2.1
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
//2.2
RACSignalBindBlock bindingBlock = block();
...
可以看到,在bind方法中,参数block
到结束的return都没有去调用.bind返回了一个新创建的信号.这个在代码中,体现为RACSignal *bindSignal
.
而在 return后面的 [[RACSignal createSignal:^(id<RACSubscriber> subscriber)
,这个createSignal
定义如下:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
return [RACDynamicSignal createSignal:didSubscribe];
}
可以看出,这个 createSignal:
需要一个didSubscribe
block,也就是在2.1处,传递的:
^(id<RACSubscriber> subscriber) {
RACSignalBindBlock bindingBlock = block();
...
继续看下去,[RACDynamicSignal createSignal:didSubscribe];
这句中,createSignal
的实现如下:
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}
就是说,创建了一个 RACDynamicSignal
类型的对象,然后将didSubscribe
block赋值给它的_didSubscribe
属性. 属性定义如下:
@property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
到此,这个bind流程走完了.
总结就是:
bind方法传递 RACSignalBindBlock
类型的参数到了 createSignal
的参数block中, createSingal 的参数block被保存为 RACDynamicSignal 的didSubscribe属性.
注意,到此为止,没有任何block获得执行.也就是如果代码单纯如下:
// 1.
RACSubject *subject = [RACSubject subject];
// 2.
RACSignal *bindSignal = [subject bind:^RACSignalBindBlock _Nonnull {
//breakpoint_1
return ^RACSignal *( id value, BOOL *stop ) {
NSLog(@"接收到源信号的内容: %@",value);
//breakpoint_2
return [RACReturnSignal return:value];
};
}];
打在两个block中的两个breakpoint都不会执行.
- 订阅绑定信号
[bindSignal subscribeNext:^( id _Nullable x ){
NSLog(@"订阅的地方,也就是处理完的信号:%@",x);
}];
订阅信号,这个subscribeNext
方法如下:
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL);
//3.1
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
//3.2
return [self subscribe:o];
}
可以看到,方法内部创建了一个RACSubscriber
订阅者.这个创建方法subscriberWithNext
定义如下:
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
RACSubscriber *subscriber = [[self alloc] init];
subscriber->_next = [next copy];
subscriber->_error = [error copy];
subscriber->_completed = [completed copy];
return subscriber;
}
可以看到,就是将对应的 next ,error,complete三种类型的block保存到订阅者中.在这里,我们的nextBlock就是:
^( id _Nullable x ){
NSLog(@"订阅的地方,也就是处理完的信号:%@",x);
}];
也就是说,将我们的以上block保存到订阅者中.
在3.1之后, 3.2 [self subscribe:o]
这是真正的去订阅信号.订阅信号,会触发信号的didSubscribe
.也就是代码2.2所在的block会获得执行,而根据刚刚的线路.代码2.2中有RACSignalBindBlock bindingBlock = block();
,也就是我们传递的返回值是RACSignalBindBlock
类型的block会执行.也就是 breakPoint_1会执行 ,而代码中 block()
的返回值是RACSignalBindBlock
,代码将其保存到了bindingBlock
这个变量中.
在bind方法末尾:
@autoreleasepool {
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
//3.3
RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
// Manually check disposal to handle synchronous errors.
if (compoundDisposable.disposed) return;
BOOL stop = NO;
//3.4
id signal = bindingBlock(x, &stop);
@autoreleasepool {
if (signal != nil) addSignal(signal);
if (signal == nil || stop) {
[selfDisposable dispose];
completeSignal(selfDisposable);
}
}
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(selfDisposable);
}
}];
selfDisposable.disposable = bindingDisposable;
}
```
3.3 `RACDisposable *bindingDisposable = [self subscribeNext:^(id x) ` self 代指的调用者,也就是我们的源信号.订阅这个信号,也是会创建一个订阅者.也是用来保存block的.以上代码中,nextBlock , errorBlock, completeBlock都有,都会被保存.
而nextBlock做了:
```objc
id signal = bindingBlock(x, &stop);
@autoreleasepool {
//4.5
if (signal != nil) addSignal(signal);
if (signal == nil || stop) {
[selfDisposable dispose];
completeSignal(selfDisposable);
}
}
这样订阅流程就走完了.
4.源信号发送数据 [subject sendNext:@"123"];
这样会执行源信号订阅者的nextBlock,而nextBlock中有 bindingBlock的执行.bindingBlock有个x参数,这就是我们send的 @"123".
注意4.5这里有个判空的过程:
if (signal != nil) addSignal(signal);
如果在2.2我们返回了空信号,那么addSignal(signal)
是不会得到执行的.
然后4.5这个bingBlock的返回值作为参数,传递到了addSignal
中,addSignal
的定义如下:
void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
OSAtomicIncrement32Barrier(&signalCount);
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
RACDisposable *disposable = [signal subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(selfDisposable);
}
}];
selfDisposable.disposable = disposable;
};
内部,订阅了我们的处理后的信号(我们开始代码的bindSignal).这样新信号的didSubscribe block 就会得到执行.
这就是使用bind进行了一次信号的完整流程.