RACSignal的使用方法 - 对RACStream的实现(一)

RACSignal有很多的方法,包括对父类RACStream方法的实现、RACSignal.m中的方法、RACSignal+Operations.m中的方法,今天先分析下是如何实现RACStream中的方法的。


RACStream的介绍

打开RACStream.h文件,可以看到里面都很多的方法,然后打开RACStream.m里面,看看各个方法的调用,就会发现其实众多的方法最终都是调用了flattenMap:bind:两个方法,而flattenMap:里面也是调用了bind:方法,所以重点还是要分析bind:方法的实现。RACStream并没有对bind:实现什么逻辑,所以还是要到RACSignal中查看bind:的实现。


RACSignalbind:的实现

首先看下方法里面的注释:

/*
 * -bind: should:
 * 
 * 1. Subscribe to the original signal of values.
 * 2. Any time the original signal sends a value, transform it using the binding block.
 * 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
 * 4. If the binding block asks the bind to terminate, complete the _original_ signal.
 * 5. When _all_ signals complete, send completed to the subscriber.
 * 
 * If any signal sends an error at any point, send that to the subscriber.
 */

翻译一下:

1. 订阅源信号的值
2. 不管源信号什么时候发送一个值,都使用 binding block 转换这个值。
3. 如果转换后的值是一个信号,订阅这个信号并将这个信号发送的值转换为订阅者需要接收的值。
4. 如果 binding block 想要终止 bind,源信号完成。
5. 当所有的信号完成了,给订阅者发送完成操作。
* 如果任何一个信号任何时间发送了一个错误,需要将这个错误发送给订阅者。

接着,看下代码具体操作:
* 首先通过createSignal:重新创建了一个信号。然后在信号的_didSubscribe中完成了所有的操作。
* _didSubscribe中首先调用bind:方法的参数block获取到了RACStreamBindBlock类型的bindingBlockRACStreamBindBlock的定义为typedef RACStream * (^RACStreamBindBlock)(id value, BOOL *stop);,有两个参数value stop和一个返回值RACStreamBindBlock,具体使用会在下面的逻辑中体现出来。
* 接着创建了一个数组用来存放信号,初始化中存放了信号本身。NSMutableArray *signals = [NSMutableArray arrayWithObject:self];
* 创建一个整合清理对象RACCompoundDisposable用于管理所有涉及到的清理对象。RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
* 接着一个completeSignal块,用来处理完成逻辑。包含两个参数RACSignal *signal, RACDisposable *finishedDisposable,首先将signal移除,然后判断当前信号数组signals的个数,如果一个信号都没有了,就发送完成信号,并将所有的清理对象全部清除。如果数组中还有其他信号的话,就移除该信号对应的清理对象。
* 接着是addSignal块,用来处理信号的添加。包含一个参数RACSignal *signal,首先将信号添加的数组中signals,然后创建一个清理对象selfDisposable并添加到compoundDisposable中,接下来通过subscribeNext:error:completed:完成对该信号的订阅,将该信号的值发送出去,如果遇到错误,将compoundDisposable清除,然后发送错误信息。如果遇到完成信息,通过定义的completeSignal块处理。
* 接下来就是对应源信号的订阅。首先还是创建一个对应于原信号的清理对象selfDisposable并将其添加到整合清理对象compoundDisposable中。接着在对源信号的订阅中,subscribeNext:首先检查compoundDisposable是否已经做了清理工作,然后调用bindingBlock完成对信号值的转换,如果转换后的值是一个信号,调用addSignal块将该信号处理,如果不是个信号或者得到的了终止信息,清理源信号并调用completeSignal处理。error:中调用整合清理对象的清理方法,并发送一个错误信息。completed中还是调用completeSignal块处理。

虽然是把整个方法从上到下分析了一遍,但是并没有跟注释对应上,所以,现在从注释的角度分享下代码。

1. 订阅源信号的值

这一点应该是不用说了,通过self subscribeNext:订阅了源信号的值。

2. 不管源信号什么时候发送一个值,都使用 binding block 转换这个值。

看下源信号的订阅,subscribeNext:id signal = bindingBlock(x, &stop);调用了bindingBlock完成了对值的转换。

3. 如果转换后的值是一个信号,订阅这个信号并将这个信号发送的值转换为订阅者需要接收的值。

同样接着看subscribeNext:,

if (signal != nil) addSignal(signal);
if (signal == nil || stop) {
    [selfDisposable dispose];
    completeSignal(self, selfDisposable);
}

这里检测signal != nil,然后addSignal(signal)。在addSignal定义中signal subscribeNext:完成了对signal的订阅,并将值发送了出去。

4. 如果 binding block 想要终止 bind,源信号完成。

在看self subscribeNext:if (signal == nil || stop),如果stopYES,就会调用[selfDisposable dispose];完成对源信号的订阅。

5. 当所有的信号完成了,给订阅者发送完成操作。

查看completeSignal块的定义,里面会判断当前信号的个数,如果没有了信号,就会发送完成信息并做清理工作。

* 如果任何一个信号任何时间发送了一个错误,需要将这个错误发送给订阅者。

首先看下源信号发生错误,会完成所有清理工作并发送错误信息。此时就算是signals中还有其他的信号,也不会在发送任何信息,因为已经被compoundDisposable作了清理。然后看下添加信号的订阅signal subscribeNext:error:,错误的时候也会做清理工作并发送错误信息。此时如果源信号的订阅在继续,在self subscribeNext:中会判断compoundDisposable.disposed,防止继续发送信息。其实到这里,应该还是有个疑问就是,为什么对添加的信号里面没有做compoundDisposable.disposed的校验呢?注释中说明// Manually check disposal to handle synchronous errors.,我不明白具体是什么原因,目前感觉是没有必要的。

上面把bind:方法的实现分析完了,然后回到RACStream中查看flattenMap:的实现。

- (instancetype)flattenMap:(RACStream * (^)(id value))block {
    Class class = self.class;

    return [[self bind:^{
        return ^(id value, BOOL *stop) {
            id stream = block(value) ?: [class empty];
            NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);

            return stream;
        };
    }] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}

可见,里面还是调用了bind:方法,通过参数block可以将原始信号发送的信息转换成一个信号并对其进行订阅。而block可以由外部决定怎么创建一个RACStream

猜你喜欢

转载自blog.csdn.net/jianghui12138/article/details/80987109