ReactiveCocoa之集合使用详解02

  • 上一篇ReactiveCocoa使用详解01提到了, RACStream中有两个子类——RACSignalRACSequence
  • 上一篇文章中只介绍了, 关于RACSignal的使用和底层实现原理
  • 这里我们就主要学习一下RACSequence的使用和底层实现
  • GitHub上的Demo地址

关于RACTuple

  • 这里在介绍RACSequence之前,我们先来看看RACTuple的介绍和实现吧! 在RAC中RACTupleReactiveCocoa的元组类
  • 在Swift中, 元组类是一种很重要也很常用的类型, 是一种以下标访问成员变量的类型
1
2
3
4
5
6
7
8
9
10
//swift中的元组
let tuple = (3, 2, "a")
print(tuple)
//输出: (3, 2, "a")

let tuple1 = tuple.0
let tuple2 = tuple.2
print(tuple1, tuple2)
//输出: 3 a

RAC中的元组–RACTuple

在RAC中RACTuple是继承自NSObject, 并遵循协议NSCoding, NSCopying, NSFastEnumeration的类, 如下

1
2
3
4
5
6
7
8
9
10
11
12
@interface RACTuple : NSObject <NSCoding, NSCopying, NSFastEnumeration>

//元组成员的个数
@property (nonatomic, readonly) NSUInteger count;

@property (nonatomic, readonly, nullable) id first;
@property (nonatomic, readonly, nullable) id second;
@property (nonatomic, readonly, nullable) id third;
@property (nonatomic, readonly, nullable) id fourth;
@property (nonatomic, readonly, nullable) id fifth;
@property (nonatomic, readonly, nullable) id last;

使用参考

1
2
3
4
5
6
7
8
RACTuple *tuple = RACTuplePack(@1, @2, @"32", @23, @"jun", @2.3, @4.56, @100);
NSLog(@"%lu", (unsigned long)tuple.count);
NSLog(@"%@--%@--%@", tuple.first, tuple.last, tuple[6]);

/*输出:
2018-03-19 20:19:49.139932+0800 ReactiveObjc[23307:1441026] 8
2018-03-19 20:19:49.140112+0800 ReactiveObjc[23307:1441026] 1--100--4.56
*/

RACTuple透过底层看上去, 其实就是一个NSArray在进行操作, 无非是针对该数组进行了一些不同的封装和处理

1
2
3
4
5
6
7
8
@interface RACTuple ()

- (instancetype)initWithBackingArray:(NSArray *)backingArray NS_DESIGNATED_INITIALIZER;

@property (nonatomic, readonly) NSArray *backingArray;

@end

  • 下面我们看一下RACTuple提供的类方法和实例方法
  • 一共3个实例方法, 3个类方法, 如下
1
2
3
4
5
6
7
8
9
/// 类方法
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array;
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert;
+ (instancetype)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;

/// 实例方法
- (nullable id)objectAtIndex:(NSUInteger)index;
- (NSArray *)allObjects;
- (__kindof RACTuple *)tupleByAddingObject:(nullable id)obj;
  • 下面我们一个一个简单介绍下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array {
return [self tupleWithObjectsFromArray:array convertNullsToNils:NO];
}

+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert {
if (!convert) {
return [[self alloc] initWithBackingArray:array];
}

NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
for (id object in array) {
[newArray addObject:(object == NSNull.null ? RACTupleNil.tupleNil : object)];
}

return [[self alloc] initWithBackingArray:newArray];
}
  • 这两个方法都是根据传入的array初始化为RACTuple内部的NSArray
  • 然而这两个初始化方法唯一的不同点就在于convert参数, 区别在于是否把NSNull转换成RACTupleNil类型
  • 这里还有一个需要注意的就是RACTupleNil, 是一个单例
1
2
3
4
5
6
7
8
9
+ (RACTupleNil *)tupleNil {
static dispatch_once_t onceToken;
static RACTupleNil *tupleNil = nil;
dispatch_once(&onceToken, ^{
tupleNil = [[self alloc] init];
});

return tupleNil;
}

最后一个类方法, 与NSArray的类方法相同, 如下:

1
2
+ (instancetype)arrayWithObjects:(ObjectType)firstObj, ... NS_REQUIRES_NIL_TERMINATION;
+ (instancetype)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;

简单使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    NSArray *arr = [NSArray arrayWithObjects:@1, NSNull.null, @2, @"jun", nil];
RACTuple *tuple1 = [RACTuple tupleWithObjectsFromArray:arr];
RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:arr convertNullsToNils:YES];
NSLog(@"%@", tuple1.second);
NSLog(@"%@", tuple2.second);

RACTuple *tuple3 = [RACTuple tupleWithObjects:@1, @3.4, @"jun", nil];
NSLog(@"%lu", (unsigned long)tuple3.count);

/*输出结果:
2018-03-19 20:59:14.995245+0800 ReactiveObjc[24150:1545073] <null>
2018-03-19 20:59:14.995557+0800 ReactiveObjc[24150:1545073] (null)
2018-03-19 20:59:14.995866+0800 ReactiveObjc[24150:1545073] 3
*/

RACTuple的相关类–RACTupleUnpackingTrampoline

关于RACTuple还有2个相关的类,RACTupleUnpackingTrampolineRACTupleSequence

这里我们先看一下, 该类的属性和方法

1
2
3
4
5
6
@interface RACTupleUnpackingTrampoline : NSObject

+ (instancetype)trampoline;
- (void)setObject:(nullable RACTuple *)tuple forKeyedSubscript:(NSArray *)variables;

@end

可以看到只有一个单例和一个示例方法, 下面看依稀阿底层的具体实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+ (instancetype)trampoline {
static dispatch_once_t onceToken;
static id trampoline = nil;
dispatch_once(&onceToken, ^{
trampoline = [[self alloc] init];
});

return trampoline;
}

- (void)setObject:(RACTuple *)tuple forKeyedSubscript:(NSArray *)variables {
NSCParameterAssert(variables != nil);

[variables enumerateObjectsUsingBlock:^(NSValue *value, NSUInteger index, BOOL *stop) {
__strong id *ptr = (__strong id *)value.pointerValue;
*ptr = tuple[index];
}];
}

该实例方法会遍历传入的NSArray数组, 然后依次取出每一个value的指针, 用这个指针又赋值给了tuple[index], 下面我们就看一下这个方法的具体使用方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- (void)setUnpackingTrampoline {
RACTupleUnpackingTrampoline *line = [RACTupleUnpackingTrampoline trampoline];
NSString *str1;
NSString *str2;
NSString *str3;
NSArray *arr = [NSArray arrayWithObjects:[NSValue valueWithPointer:&str1], [NSValue valueWithPointer:&str2], [NSValue valueWithPointer:&str3], nil];

NSLog(@"处理之前: str1 = %@, str2 = %@, str3 = %@", str1, str2, str3);
[line setObject:RACTuplePack(@"tian", @23, @3.45) forKeyedSubscript:arr];
NSLog(@"处理之后: str1 = %@, str2 = %@, str3 = %@", str1, str2, str3);

/*输出结果:
2018-03-20 15:43:28.785571+0800 ReactiveObjc[7074:641560] 处理之前: str1 = (null), str2 = (null), str3 = (null)
2018-03-20 15:43:28.786078+0800 ReactiveObjc[7074:641560] 处理之后: str1 = tian, str2 = 23, str3 = 3.45
*/
}

这个方法的作用类似于, 把封装好的RACTuple对象, 一个一个的把它的成员变量解析出来, 说到这里我们就不得不提及两个宏

RACTuple中的宏

一般使用用两个宏,RACTupleUnpack( ) 用来解包,Rc( ) 用来装包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#define RACTuplePack(...) \
RACTuplePack_(__VA_ARGS__)

//下面RACTuplePack_底层的调用
#define RACTuplePack_(...) \
([RACTuplePack_class_name(__VA_ARGS__) tupleWithObjectsFromArray:@[ metamacro_foreach(RACTuplePack_object_or_ractuplenil,, __VA_ARGS__) ]])


//下面RACTupleUnpack_底层的调用
#define RACTupleUnpack_(...) \
metamacro_foreach(RACTupleUnpack_decl,, __VA_ARGS__) \
\
int RACTupleUnpack_state = 0; \
\
RACTupleUnpack_after: \
; \
metamacro_foreach(RACTupleUnpack_assign,, __VA_ARGS__) \
if (RACTupleUnpack_state != 0) RACTupleUnpack_state = 2; \
\
while (RACTupleUnpack_state != 2) \
if (RACTupleUnpack_state == 1) { \
goto RACTupleUnpack_after; \
} else \
for (; RACTupleUnpack_state != 1; RACTupleUnpack_state = 1) \
[RACTupleUnpackingTrampoline trampoline][ @[ metamacro_foreach(RACTupleUnpack_value,, __VA_ARGS__) ] ]

  • 这里的解包的宏的底层实现就是上面说到的RACTupleUnpackingTrampoline的实例方法
  • 关于ACTuplePack的使用这里也不在多说了, 下面主要看一下用于解包的宏, 看一下主要用法, 上代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//宏的使用
RACTuple *tuple4 = RACTuplePack(@"tian", @23);
RACTupleUnpack(NSString *str1, NSNumber *num1) = tuple4;
NSLog(@"%@--%d", str1, num1.intValue);
///输出: tian--23

RACTuple *tuple3 = [RACTuple tupleWithObjects:@"jun", @3.4, nil];
RACTupleUnpack(NSString *str2, NSNumber *num2) = tuple3;
NSLog(@"%@--%.2f", str2, num2.floatValue);
///输出: jun--3.40

/// 上面的两种做法等同于下面这种做法
NSString *str3 = tuple3[0];
NSNumber *num3 = tuple3[1];
NSLog(@"%@--%.2f", str3, num3.floatValue);
///输出: jun--3.40

RACTupleSequence

  • 上面提到了RACTuple还有2个相关的类,RACTupleUnpackingTrampolineRACTupleSequence
  • RACTupleUnpackingTrampoline上面我们已经介绍过了
  • 这里我们来介绍一下RACTupleSequence
    • 之所以说RACTupleSequenceRACTuple相关, 也只是因为两者的雷鸣里面都有一个Tuple
    • 实际上RACTupleSequence是继承自RACSequence的, 下面看一下定义代码, 只有一个返回值为RACSequence的类方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#import "RACSequence.h"

+ (RACSequence *)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset;

@end


//方法的实现
+ (RACSequence *)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset {
NSCParameterAssert(offset <= backingArray.count);

if (offset == backingArray.count) return self.empty;

RACTupleSequence *seq = [[self alloc] init];
seq->_tupleBackingArray = backingArray;
seq->_offset = offset;
return seq;
}
  • 可见:
    • RACTupleSequence这个类的目的就是把Tuple转换成Sequence
    • Sequence里面的数组就是Tuple内部的backingArray。
    • offset从0开始

RACSequence底层实现

RACSequenceRACStream的子类,主要是ReactiveCocoa里面的集合类, 先来看看关于RACSequence的属性

RACSequence的属性

1
2
3
4
5
6
7
8
9
10
11
@property (nonatomic, strong, readonly, nullable) ValueType head;

@property (nonatomic, strong, readonly, nullable) RACSequence<ValueType> *tail;

@property (nonatomic, copy, readonly) NSArray<ValueType> *array;

@property (nonatomic, copy, readonly) NSEnumerator<ValueType> *objectEnumerator;

@property (nonatomic, copy, readonly) RACSequence<ValueType> *eagerSequence;

@property (nonatomic, copy, readonly) RACSequence<ValueType> *lazySequence;

headtail

  • RACSequence的所有属性中, 最重要的莫过于headtail两个属性了, 而tail又是一个RACSequence
  • 这两者就像一个人的头和身体两部分
  • 测试代码如下
1
2
3
4
5
6
7
8
9
10
11
12
RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^id _Nullable{
return @12;
} tailBlock:^RACSequence * _Nonnull{
return @[@23, @"jun"].rac_sequence;
}];
NSLog(@"sequence.head = %@ , sequence.tail = %@", sequence.head, sequence.tail);

/*输出结果:
sequence.head = 12 , sequence.tail = <RACArraySequence: 0x6000002325a0>{ name = , array = (
23,
jun
) }

objectEnumerator

objectEnumerator是一个快速枚举器, 看一下底层的get方法

1
2
3
4
5
6
- (NSEnumerator *)objectEnumerator {
RACSequenceEnumerator *enumerator = [[RACSequenceEnumerator alloc] init];
enumerator.sequence = self;
return enumerator;
}

  • 这里涉及到一个RACSequenceEnumerator, 底层只有一个属性
  • 为了更加方便的RACSequence进行遍历, 重写了父类的方法
  • 有了这个NSEnumerator,就可以从RACSequence的head一直遍历到tail
  • RACSequence里面定义的objectEnumerator,就是为了取出内部的RACSequenceEnumerator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@interface RACSequenceEnumerator : NSEnumerator

@property (nonatomic, strong) RACSequence *sequence;

@end

//这里重写了父类的方法
- (id)nextObject {
id object = nil;

@synchronized (self) {
object = self.sequence.head;
self.sequence = self.sequence.tail;
}

return object;
}

array

1
2
3
4
5
6
7
8
- (NSArray *)array {
NSMutableArray *array = [NSMutableArray array];
for (id obj in self) {
[array addObject:obj];
}

return [array copy];
}
  • RACSequence的定义里面还有一个array,这个数组就是返回一个NSArray
  • 这个数组里面装满了RACSequence里面所有的对象。
  • 这里之所以能用for-in,是因为实现了NSFastEnumeration协议。
  • 至于for-in的效率,完全就看重写NSFastEnumeration协议里面countByEnumeratingWithState: objects: count: 方法里面的执行效率了
  • 至于剩下的两个属性, 下文中会继续说到

RACSequence的方法

RACSequence的初始化方法

RACSequence的初始化方法有且只有一个

1
2
3
+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence<id> *(^)(void))tailBlock {
return [[RACDynamicSequence sequenceWithHeadBlock:headBlock tailBlock:tailBlock] setNameWithFormat:@"+sequenceWithHeadBlock:tailBlock:"];
}

RACDynamicSequence属性

上面初始化方法的底层是直接调用了RACDynamicSequence的一个类方法, 而这个类又是RACSequence的子类, 看看主要属性

1
2
3
4
5
6
7
8
9
10
11
@interface RACDynamicSequence () {
id _head;
RACSequence *_tail;
id _dependency;
}
@property (nonatomic, strong) id headBlock;
@property (nonatomic, strong) id tailBlock;
@property (nonatomic, assign) BOOL hasDependency;
@property (nonatomic, strong) id (^dependencyBlock)(void);

@end
  • 相比大家应该知道, 正常情况下我们定义的block都是用copy修饰的
  • 而这里, 作者定义了三个block: headBlock, tailBlockdependencyBlock都是用strong修饰的
  • 关于这个问题, 可以参考这里

方法的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence<id> *(^)(void))tailBlock {
NSCParameterAssert(headBlock != nil);

RACDynamicSequence *seq = [[RACDynamicSequence alloc] init];
seq.headBlock = [headBlock copy];
seq.tailBlock = [tailBlock copy];
seq.hasDependency = NO;
return seq;
}

+ (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock {
NSCParameterAssert(dependencyBlock != nil);
NSCParameterAssert(headBlock != nil);

RACDynamicSequence *seq = [[RACDynamicSequence alloc] init];
seq.headBlock = [headBlock copy];
seq.tailBlock = [tailBlock copy];
seq.dependencyBlock = [dependencyBlock copy];
seq.hasDependency = YES;
return seq;
}
  • hasDependency这个变量是代表是否有dependencyBlock。这个函数里面就只把headBlocktailBlock保存起来了
  • 上面第二个方法是带有dependencyBlock的, 也会把dependencyBlock保存起来

积极运算和惰性求值

  • 说到惰性求值, 就立马想到了懒加载, 就是在getter里动态返回属性, 也就是等到要用的时候才会计算
  • 关于这两个概念, 推荐大家看这篇文章聊一聊iOS开发中的惰性计算

积极运算

RACSequence中积极运算的代表是RACSequence的一个子类RACArraySequence的子类——RACEagerSequence。它的积极运算表现在其bind函数上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
- (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];
}
  • 可以看到, 该方法内部执行了两层for-in循环
    • 第一层循环遍历的自己RACSequence中的值,然后拿到这个值传入闭包bindBlock()中,返回一个RACSequence,最后用一个NSMutableArray依次把每个RACSequence里面的值都装起来
    • 第二层循环是在遍历RACSequence,之所以可以用for-in的方式遍历就是因为实现了NSFastEnumeration协议,实现了countByEnumeratingWithState: objects: count: 方法
  • 这里就是一个积极运算的例子,在每次循环中都会把闭包block()的值计算出来。值得说明的是,最后返回的RACSequence的类型是self.class类型的,即还是RACEagerSequence类型的

惰性计算

等到需要用到的时候才会计算, 我们看一下在RACSequence中,bind函数的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
- (RACSequence *)bind:(RACSequenceBindBlock (^)(void))block {
RACSequenceBindBlock bindBlock = block();
return [[self bind:bindBlock passingThroughValuesFromSequence:nil] setNameWithFormat:@"[%@] -bind:", self.name];
}

- (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;
}
  • 在上述方法实现中, 就是用sequenceWithLazyDependency: headBlock: tailBlock:方法生成了一个RACSequence,并返回
  • 通过调用RACSequence里面的bind操作,并没有执行3个闭包里面的值,只是保存起来了。
  • 这里就是惰性求值的表现——等到要用的时候才会计算
  • 下面我们看一段代码示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    NSArray *arr = @[@1, @3, @4];
RACSequence *sequence1 = [arr.rac_sequence map:^id _Nullable(id _Nullable value) {
NSLog(@"sequence");
return @10;
}];

RACSequence *lazySequence = [arr.rac_sequence.lazySequence map:^id _Nullable(id _Nullable value) {
NSLog(@"lazySequence");
return @20;
}];

RACSequence *eagerSequence = [arr.rac_sequence.eagerSequence map:^id _Nullable(id _Nullable value) {
NSLog(@"eagerSequence");
return @30;
}];

// [sequence1 array];
// [lazySequence array];

/*输出:
2018-03-21 15:53:24.562184+0800 ReactiveObjc[9109:771797] eagerSequence
2018-03-21 15:53:24.562674+0800 ReactiveObjc[9109:771797] eagerSequence
2018-03-21 15:53:24.562799+0800 ReactiveObjc[9109:771797] eagerSequence
*/
  • 从打印结果可以看出,只有eagerSequence执行了三次, 而其他两个并没有输出
  • 原因是因为bind闭包只在eagerSequence中真正被调用执行了,而在lazySequence中bind闭包仅仅只是被copy了
  • 当吧最后两行注释打开之后
  • 可见在RACSequence中,除去RACEagerSequence是积极运算,其他的Sequence都是惰性求值的。
1
2
3
4
5
6
7
8
9
2018-03-21 15:53:24.562184+0800 ReactiveObjc[9109:771797] eagerSequence
2018-03-21 15:53:24.562674+0800 ReactiveObjc[9109:771797] eagerSequence
2018-03-21 15:53:24.562799+0800 ReactiveObjc[9109:771797] eagerSequence
2018-03-21 15:53:24.562940+0800 ReactiveObjc[9109:771797] sequence
2018-03-21 15:53:24.563403+0800 ReactiveObjc[9109:771797] sequence
2018-03-21 15:53:24.563583+0800 ReactiveObjc[9109:771797] sequence
2018-03-21 15:53:24.563742+0800 ReactiveObjc[9109:771797] lazySequence
2018-03-21 15:53:24.563838+0800 ReactiveObjc[9109:771797] lazySequence
2018-03-21 15:53:24.563937+0800 ReactiveObjc[9109:771797] lazySequence

RACSequence的方法

1
2
3
4
5
6
7
8
9
- (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;

- (BOOL)any:(BOOL (^)(ValueType _Nullable value))block;

- (BOOL)all:(BOOL (^)(ValueType _Nullable value))block;

- (nullable ValueType)objectPassingTest:(BOOL (^)(ValueType _Nullable value))block;

折叠函数

我们先看一下他的底层实现, 函数传入了一个初始值start,然后依次循环执行reduce( ),循环之后,最终的值作为返回值返回。第一个函数就是折叠函数,从左边折叠到右边; 第二个方向是从右往左

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- (id)foldLeftWithStart:(id)start reduce:(id (^)(id, id))reduce {
NSCParameterAssert(reduce != NULL);

if (self.head == nil) return start;

for (id value in self) {
start = reduce(start, value);
}

return start;
}

- (id)foldRightWithStart:(id)start reduce:(id (^)(id, RACSequence *))reduce {
NSCParameterAssert(reduce != NULL);

if (self.head == nil) return start;

RACSequence *rest = [RACSequence sequenceWithHeadBlock:^{
if (self.tail) {
return [self.tail foldRightWithStart:start reduce:reduce];
} else {
return start;
}
} tailBlock:nil];

return reduce(self.head, rest);
}

具体使用方法测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (void)setSequenceAction {
NSArray *array = @[@5, @3, @9, @4];
RACSequence *sequence = [array rac_sequence];
id leftData = [sequence foldLeftWithStart:@"-" reduce:^id _Nullable(id _Nullable accumulator, id _Nullable value) {
return [accumulator stringByAppendingString:[value stringValue]];
}];

id rightData = [sequence foldRightWithStart:@":" reduce:^id _Nullable(id _Nullable first, RACSequence * _Nonnull rest) {
return [NSString stringWithFormat:@"%@-%@", rest.head, first];
}];

NSLog(@"leftData = %@, rightData = %@", leftData, rightData);

//输出: leftData = -5394, rightData = :-4-9-3-5
}

objectPassingTest

函数里面会调用RACStream中的filter:函数, 如果block(value)为YES,就代表通过了Test,那么就会返回value的sequence, 取出head返回

1
2
3
4
5
- (id)objectPassingTest:(BOOL (^)(id))block {
NSCParameterAssert(block != NULL);

return [self filter:block].head;
}

测试代码如下

1
2
3
4
5
6
7
8
9
NSArray *array = @[@5, @3, @9, @4];
RACSequence *sequence = [array rac_sequence];
id anyData = [sequence objectPassingTest:^BOOL(id _Nullable value) {
NSLog(@"%@", value);
return false;
}];
NSLog(@"%@", anyData);

//输出: 5

any: 和 all:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
- (BOOL)any:(BOOL (^)(id))block {
NSCParameterAssert(block != NULL);

return [self objectPassingTest:block] != nil;
}

- (BOOL)all:(BOOL (^)(id))block {
NSCParameterAssert(block != NULL);

NSNumber *result = [self foldLeftWithStart:@YES reduce:^(NSNumber *accumulator, id value) {
return @(accumulator.boolValue && block(value));
}];

return result.boolValue;
}
  • any: 会调用objectPassingTest:函数,如果不为nil就代表有value值通过了Test,有通过了value的就返回YES,反之返回NO
  • all:会从左往右依次对每个值进行block( ) Test,然后每个值依次进行&&操作
  • 测试代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
NSArray *array = @[@5, @3, @9, @4];
RACSequence *sequence = [array rac_sequence];

//all
BOOL anyBool = [sequence any:^BOOL(id _Nullable value) {
return true;
}];
BOOL allBool = [sequence all:^BOOL(id _Nullable value) {
return true;
}];

NSLog(@"any = %d, all = %d", anyBool, allBool);
//输出: any = 1, all = 1

RACSequence的子类和扩展

子类

  • 关于RACSequence有以下9个子类
  • 其中RACEagerSequence是继承自RACArraySequence
  • 这些子类看名字就知道sequence里面装的是什么类型的数据。
  • RACUnarySequence里面装的是单元sequence, 它只有head值,没有tail值

RACStream.png

下面列出了每一个子类里面的方法, 前面都已经介绍过这些方法, 这里也就不在赘述

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
//RACArraySequence
@interface RACArraySequence : RACSequence

+ (RACSequence *)sequenceWithArray:(NSArray *)array offset:(NSUInteger)offset;

@end


//RACDynamicSequence
@interface RACDynamicSequence : RACSequence

+ (RACSequence *)sequenceWithLazyDependency:(id (^)(void))dependencyBlock headBlock:(id (^)(id dependency))headBlock tailBlock:(RACSequence *(^)(id dependency))tailBlock;

@end


//RACEmptySequence
@interface RACEmptySequence : RACSequence
//单例
+ (RACEmptySequence *)empty;

@end


//RACIndexSetSequence
@interface RACIndexSetSequence : RACSequence

+ (RACSequence *)sequenceWithIndexSet:(NSIndexSet *)indexSet;

@end


//RACSignalSequence
@interface RACSignalSequence : RACSequence

+ (RACSequence *)sequenceWithSignal:(RACSignal *)signal;

@end


//RACStringSequence
@interface RACStringSequence : RACSequence

+ (RACSequence *)sequenceWithString:(NSString *)string offset:(NSUInteger)offset;

@end


//RACTupleSequence
@interface RACTupleSequence : RACSequence

+ (RACSequence *)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset;

@end


//RACUnarySequence
@interface RACUnarySequence : RACSequence

+ (RACUnarySequence *)return:(id)value;

@end

扩展

RACSequenceAdditions 总共有7个Category。这7个Category分别对iOS 里面的集合类进行了RACSequence的扩展,使我们能更加方便的使用RACSequence

RACSequenceAdditions.png

NSArray+RACSequenceAdditions

1
2
3
4
5
@interface NSArray<__covariant ObjectType> (RACSequenceAdditions)

@property (nonatomic, copy, readonly) RACSequence<ObjectType> *rac_sequence;

@end

把任意一个NSArray数组转换成RACSequence, 底层是RACArraySequence调用 sequenceWithArray方法, 将NSArray对象转成RACArraySequence对象

1
2
3
- (RACSequence *)rac_sequence {
return [RACArraySequence sequenceWithArray:self offset:0];
}

NSDictionary+RACSequenceAdditions

1
2
3
4
5
6
7
@interface NSDictionary<__covariant KeyType, __covariant ObjectType> (RACSequenceAdditions)

@property (nonatomic, copy, readonly) RACSequence<RACTwoTuple<KeyType, ObjectType> *> *rac_sequence;
@property (nonatomic, copy, readonly) RACSequence<KeyType> *rac_keySequence;
@property (nonatomic, copy, readonly) RACSequence<ObjectType> *rac_valueSequence;

@end

把任意一个NSDictionary字典转换成RACSequence

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- (RACSequence *)rac_sequence {
NSDictionary *immutableDict = [self copy];

// TODO: First class support for dictionary sequences.
return [immutableDict.allKeys.rac_sequence map:^(id key) {
id value = immutableDict[key];
return RACTuplePack(key, value);
}];
}

- (RACSequence *)rac_keySequence {
return self.allKeys.rac_sequence;
}

- (RACSequence *)rac_valueSequence {
return self.allValues.rac_sequence;
}
  • rac_sequence: 通过map映射
    • 先将每一个键值对转成RACTuple元组对象, key对应元组的第一个, value对应第二个
    • 将每一个RACTuple元组放在一个数组里面
    • 最后把数组转成RACSequence对象
  • rac_keySequence: 把所有的key值转成RACSequence对象
  • rac_valueSequence: 把所有的value值转成RACSequence对象

NSSet+RACSequenceAdditions

把任意一个NSSet对象转换成RACSequence对象

1
2
3
4
5
6
7
8
9
10
11
12
@interface NSSet<__covariant ObjectType> (RACSequenceAdditions)

@property (nonatomic, copy, readonly) RACSequence<ObjectType> *rac_sequence;

@end


//属性的getter方法
- (RACSequence *)rac_sequence {
// TODO: First class support for set sequences.
return self.allObjects.rac_sequence;
}

NSString+RACSequenceAdditions

把任意一个NSString转换成包含该字符串, 所有字符的数组对应的RACSequence

1
2
3
4
5
6
7
8
9
10
11
@interface NSString (RACSequenceAdditions)

@property (nonatomic, copy, readonly) RACSequence<NSString *> *rac_sequence;

@end


//属性的getter方法
- (RACSequence *)rac_sequence {
return [RACStringSequence sequenceWithString:self offset:0];
}

NSEnumerator+RACSequenceAdditions

  • 把任意一个NSEnumerator转换成RACSequence
    • 返回的RACSequence的head是当前的sequence的head
    • 返回的RACSequence的tail是当前的sequence本身
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface NSEnumerator<ObjectType> (RACSequenceAdditions)

@property (nonatomic, copy, readonly) RACSequence<ObjectType> *rac_sequence;

@end

//底层实现
- (RACSequence *)rac_sequence {
return [RACSequence sequenceWithHeadBlock:^{
return [self nextObject];
} tailBlock:^{
return self.rac_sequence;
}];
}

NSIndexSet+RACSequenceAdditions

把任意一个NSIndexSet转换成RACSequence

1
2
3
4
5
6
7
8
9
10
11
@interface NSIndexSet (RACSequenceAdditions)

@property (nonatomic, copy, readonly) RACSequence<NSNumber *> *rac_sequence;

@end


//底层实现
- (RACSequence *)rac_sequence {
return [RACIndexSetSequence sequenceWithIndexSet:self];
}

NSOrderedSet+RACSequenceAdditions

把任意一个NSOrderedSet中的数组转换成RACSequence对象

1
2
3
4
5
6
7
8
9
10
11
12
@interface NSOrderedSet<__covariant ObjectType> (RACSequenceAdditions)

@property (nonatomic, copy, readonly) RACSequence<ObjectType> *rac_sequence;

@end


//底层实现
- (RACSequence *)rac_sequence {
// TODO: First class support for ordered set sequences.
return self.array.rac_sequence;
}

总结