ReactiveObjC - RACSequence
RACSequence代表的是RAC中一组不可变的数据的集合。如果不是特别指出,序列会根据需求懒惰的评估所包含的值,序列默认就是懒惰的。
RACSequence主要的方法和属性
head、tail
序列当前的偏移量对应的值即为head,序列当前偏移量+1对应的序列即为tail。注意head是一个值,tail是一个序列。
array
array就是把一个序列当中的值用array的形式取出来。
eagerSequence
@property (nonatomic, copy, readonly) RACSequence<ValueType> *eagerSequence;
把一个序列转换为一个热序列,如果序列本身是热序列则返回自身。热序列会立即评估所包含的所有值。
lazySequence
@property (nonatomic, copy, readonly) RACSequence<ValueType> *lazySequence;
把一个序列转换为一个懒序列,如果序列本身是懒序列则返回自身。懒序列会根据需求,在被调用时评估值。
reduce
reduce的作用是给定某个初始值i和一个处理函数p,然后对数组进行遍历,每次都是上次p函数执行的结果加上一个新值。用数学表达式就是:result = p(p(p(i,1), 2), 3)。
foldLeft、foldRight
- (id)foldLeftWithStart:(nullable id)start reduce:(id _Nullable (^)(id _Nullable accumulator, ValueType _Nullable value))reduce;
- (id)foldRightWithStart:(nullable id)start reduce:(id _Nullable (^)(id _Nullable first, RACSequence *rest))reduce;
根据传入的start值进行reduce操作,两个函数开始的方向不同。foldLeft每次都会调用reduce的block;foldRight会在内部进行递归,调用一次block,返回序列的第一个值first,rest.head是除first外已经按照reduce block中操作后的结果。
all、any
BOOL result = [sequence any:^BOOL(id value) { return YES; }];
result =[sequence all:^BOOL(id value) { return YES; }];
all和any是用来监测序列的。
如果有一个值满足条件,block返回YES,则any返回YES。
如果所有值满足条件,block全都返回YES,则all返回YES。
objectPassingTest
- (nullable ValueType)objectPassingTest:(BOOL (^)(ValueType _Nullable value))block;
在block中检测序列中的值是否满足要求,返回第一个满足要求的值,如果没有满足要求的值便返回nil。
sequenceWithHeadBlock:tailBlock:
+ (RACSequence<ValueType> *)sequenceWithHeadBlock:(ValueType _Nullable (^)(void))headBlock tailBlock:(nullable RACSequence<ValueType> *(^)(void))tailBlock;
序列的懒加载方法,在调用序列时才从headBlock和tailBlock中返回head和tail。headBlock和tailBlock一旦执行,结果就会保存下来供下次取用,不会重复调用block。
RACSequence对RACStream中抽象属性及方法的实现
empty
RACSequence衍生出来的RACEmptySequence.empty主要作用就是表示一个空序列,该序列是一个单例,所有序列的empty方法都返回它。
return
+ (instancetype)return:(id)value;
将一个value封装为序列。内部实现这里专门引入了一个类RACUnarySequence来表示只包含一个值的序列。
bind
RACStream中定义了bind,bind函数是RAC中最核心的函数,很多行为操作都是建立在bind之上,所以为了能更好理解RAC源代码我们必须弄清bind函数在做什么。
bind方法一般来说不需要我们直接调用。
RACStream只定义了bind但并没有实现,子类必须自己实现bind函数。
RACSequence和RACEagerSequence的bind不同,从中也可以看出两种序列的本质区别。
RACSequence的bind操作
- (RACSequence *)bind:(RACSequenceBindBlock (^)(void))block;
- (RACSequence *)bind:(RACSequenceBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence {
__block RACSequence *valuesSeq = self;
__block RACSequence *current = passthroughSequence;
__block BOOL stop = NO;
RACSequence *sequence = [RACDynamicSequence sequenceWithLazyDependency:^ id {
while (current.head == nil) {
if (stop) return nil;
id value = valuesSeq.head;
if (value == nil) {
stop = YES;
return nil;
}
current = (id)bindBlock(value, &stop);
if (current == nil) {
stop = YES;
return nil;
}
valuesSeq = valuesSeq.tail;
}
NSCAssert([current isKindOfClass:RACSequence.class], @"-bind: block returned an object that is not a sequence: %@", current);
return nil;
} headBlock:^(id _) {
return current.head;
} tailBlock:^ id (id _) {
if (stop) return nil;
return [valuesSeq bind:bindBlock passingThroughValuesFromSequence:current.tail];
}];
sequence.name = self.name;
return sequence;
}
bind:操作内部是调用bind:passingThroughValuesFromSequence:,第二个参数传空。
懒序列head和tail的获取都发生在调用时,在调用head时先执行dependency block,然后执行headBlock,返回head值;调用tail时先执行dependency block,然后执行tailBlock,返回tail序列。
RACEagerSequence的bind操作
- (RACSequence *)bind:(RACSequenceBindBlock (^)(void))block {
NSCParameterAssert(block != nil);
RACStreamBindBlock bindBlock = block();
NSArray *currentArray = self.array;
NSMutableArray *resultArray = [NSMutableArray arrayWithCapacity:currentArray.count];
for (id value in currentArray) {
BOOL stop = NO;
RACSequence *boundValue = (id)bindBlock(value, &stop);
if (boundValue == nil) break;
for (id x in boundValue) {
[resultArray addObject:x];
}
if (stop) break;
}
return [[self.class sequenceWithArray:resultArray offset:0] setNameWithFormat:@"[%@] -bind:", self.name];
}
热序列调用bind方法时会立即循环遍历一次自身的元素。每次遍历会调用一次block,RACSequenceBindBlock返回值是一个序列,该序列也会被遍历一次取出所有值,整个过程完成后将返回所有遍历值组成的新序列。也就是说在bind方法调用时,所有的值已经被操作,这就是热序列的特点。
concat
将两个序列的值直接拼接在一起,组成新的序列。
zipWith
将两个序列对应下标的值合并成一个元组,组成的新序列所包含的值全都是元组类型,不等长的部分将被舍去,新序列长度与原较短序列的长度相同。
RACSequence对RACStreamOperations的实现
map
把信号源内容映射成一个新的内容返回。map底层调用的是flattenMap,map的返回值会作为flattenMap的block的参数。
flattenMap
把信号源内容通过block映射成一个新的序列返回,返回的序列再重新bind给RACSequence对象。
flatten
当序列中的值也是RACStream类型时,直接使用flatten将会内部进行flattenMap操作,取出内部每个序列的值重新组成一个新序列返回。