RAC的Bind方法
RAC提供了一堆可以提高开发效率的方法,比如filter
,map
,flattenMap
等值处理方法,几乎每个方法点到底,都能看到一个叫做bind
的方法.这个方法就是RAC相对底层的方法.弄明白它,对于理解RAC是非常有帮助的.
demo
1 | - (void)runTest { |
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文件中
1 | - (instancetype)init { |
_subscribers
这个是订阅者数组.
- 绑定信号
[subject bind:^RACSignalBindBlock _Nonnull{....}
这个bind点进去,里面代码如下:
1 |
|
可以看到,在bind方法中,参数block
到结束的return都没有去调用.bind返回了一个新创建的信号.这个在代码中,体现为RACSignal *bindSignal
.
而在 return后面的 [[RACSignal createSignal:^(id<RACSubscriber> subscriber)
,这个createSignal
定义如下:
1 | + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe { |
可以看出,这个 createSignal:
需要一个didSubscribe
block,也就是在2.1处,传递的:
1 | ^(id<RACSubscriber> subscriber) { |
继续看下去,[RACDynamicSignal createSignal:didSubscribe];
这句中,createSignal
的实现如下:
1 | + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe { |
就是说,创建了一个 RACDynamicSignal
类型的对象,然后将didSubscribe
block赋值给它的_didSubscribe
属性. 属性定义如下:
1 | @property (nonatomic, copy, readonly) RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber); |
到此,这个bind流程走完了.
总结就是:
bind方法传递 RACSignalBindBlock
类型的参数到了 createSignal
的参数block中, createSingal 的参数block被保存为 RACDynamicSignal 的didSubscribe属性.
注意,到此为止,没有任何block获得执行.也就是如果代码单纯如下:
1 | // 1. |
打在两个block中的两个breakpoint都不会执行.
- 订阅绑定信号
1
2
3[bindSignal subscribeNext:^( id _Nullable x ){
NSLog(@"订阅的地方,也就是处理完的信号:%@",x);
}];
订阅信号,这个subscribeNext
方法如下:
1 | - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock { |
可以看到,方法内部创建了一个RACSubscriber
订阅者.这个创建方法subscriberWithNext
定义如下:
1 | + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed { |
可以看到,就是将对应的 next ,error,complete三种类型的block保存到订阅者中.在这里,我们的nextBlock就是:
1 | ^( id _Nullable 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方法末尾:
1 | @autoreleasepool { |
3.3 RACDisposable *bindingDisposable = [self subscribeNext:^(id x)
self 代指的调用者,也就是我们的源信号.订阅这个信号,也是会创建一个订阅者.也是用来保存block的.以上代码中,nextBlock , errorBlock, completeBlock都有,都会被保存.
而nextBlock做了:
1 |
|
这样订阅流程就走完了.
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
的定义如下:
1 | void (^addSignal)(RACSignal *) = ^(RACSignal *signal) { |
内部,订阅了我们的处理后的信号(我们开始代码的bindSignal).这样新信号的didSubscribe block 就会得到执行.
这就是使用bind进行了一次信号的完整流程.