美文网首页
ReactiveCocoa之集合使用详解02

ReactiveCocoa之集合使用详解02

作者: TitanCoder | 来源:发表于2018-03-21 22:51 被阅读22次
    • 上一篇ReactiveCocoa使用详解01提到了, RACStream中有两个子类——RACSignalRACSequence
    • 上一篇文章中只介绍了, 关于RACSignal的使用和底层实现原理
    • 这里我们就主要学习一下RACSequence的使用和底层实现
    • GitHub上的Demo地址

    一. 关于RACTuple

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

    1. RAC中的元组--RACTuple

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

    @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;
    
    

    使用参考

    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在进行操作, 无非是针对该数组进行了一些不同的封装和处理

    @interface RACTuple ()
    
    - (instancetype)initWithBackingArray:(NSArray *)backingArray NS_DESIGNATED_INITIALIZER;
    
    @property (nonatomic, readonly) NSArray *backingArray;
    
    @end
    
    
    • 下面我们看一下RACTuple提供的类方法和实例方法
    • 一共3个实例方法, 3个类方法, 如下
    /// 类方法
    + (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;
    
    • 下面我们一个一个简单介绍下
    + (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, 是一个单例
    + (RACTupleNil *)tupleNil {
        static dispatch_once_t onceToken;
        static RACTupleNil *tupleNil = nil;
        dispatch_once(&onceToken, ^{
            tupleNil = [[self alloc] init];
        });
        
        return tupleNil;
    }
    

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

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

    简单使用:

        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
    */
    

    2. RACTuple的相关类--RACTupleUnpackingTrampoline

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

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

    @interface RACTupleUnpackingTrampoline : NSObject
    
    + (instancetype)trampoline;
    - (void)setObject:(nullable RACTuple *)tuple forKeyedSubscript:(NSArray *)variables;
    
    @end
    

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

    + (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], 下面我们就看一下这个方法的具体使用方法

    - (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对象, 一个一个的把它的成员变量解析出来, 说到这里我们就不得不提及两个宏

    3. RACTuple中的宏

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

    #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的使用这里也不在多说了, 下面主要看一下用于解包的宏, 看一下主要用法, 上代码
        //宏的使用
        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
    

    4. RACTupleSequence

    • 上面提到了RACTuple还有2个相关的类,RACTupleUnpackingTrampolineRACTupleSequence
    • RACTupleUnpackingTrampoline上面我们已经介绍过了
    • 这里我们来介绍一下RACTupleSequence
      • 之所以说RACTupleSequenceRACTuple相关, 也只是因为两者的雷鸣里面都有一个Tuple
      • 实际上RACTupleSequence是继承自RACSequence的, 下面看一下定义代码, 只有一个返回值为RACSequence的类方法
    #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的属性

    1. RACSequence的属性

    @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;
    

    1-1. 于headtail

    • RACSequence的所有属性中, 最重要的莫过于headtail两个属性了, 而tail又是一个RACSequence
    • 这两者就像一个人的头和身体两部分
    • 测试代码如下
        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
        ) }
    

    1-2. objectEnumerator

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

    - (NSEnumerator *)objectEnumerator {
        RACSequenceEnumerator *enumerator = [[RACSequenceEnumerator alloc] init];
        enumerator.sequence = self;
        return enumerator;
    }
    
    
    • 这里涉及到一个RACSequenceEnumerator, 底层只有一个属性
    • 为了更加方便的RACSequence进行遍历, 重写了父类的方法
    • 有了这个NSEnumerator,就可以从RACSequence的head一直遍历到tail
    • RACSequence里面定义的objectEnumerator,就是为了取出内部的RACSequenceEnumerator
    @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;
    }
    

    1-3. array

    - (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的方法

    1. RACSequence的初始化方法

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

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

    1-1. RACDynamicSequence属性

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

    @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. 方法的实现

    + (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开发中的惰性计算

    1. 积极运算

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

    2. 惰性计算

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

    - (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个闭包里面的值,只是保存起来了。
    • 这里就是惰性求值的表现——等到要用的时候才会计算
    • 下面我们看一段代码示例
        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都是惰性求值的。
    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的方法

    - (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;
    

    1. 折叠函数

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

    - (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);
    }
    
    

    具体使用方法测试代码

    - (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
    }
    

    2. objectPassingTest

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

    - (id)objectPassingTest:(BOOL (^)(id))block {
        NSCParameterAssert(block != NULL);
    
        return [self filter:block].head;
    }
    

    测试代码如下

    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
    

    3. any: 和 all:

    - (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,然后每个值依次进行&&操作
    • 测试代码如下:
        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的子类和扩展

    1. 子类

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

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

    //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
    
    

    2. 扩展

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

    RACSequenceAdditions.png

    2-1. NSArray+RACSequenceAdditions

    @interface NSArray<__covariant ObjectType> (RACSequenceAdditions)
    
    @property (nonatomic, copy, readonly) RACSequence<ObjectType> *rac_sequence;
    
    @end
    

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

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

    2-2. NSDictionary+RACSequenceAdditions

    @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

    - (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对象

    2-3. NSSet+RACSequenceAdditions

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

    @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;
    }
    

    2-4. NSString+RACSequenceAdditions

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

    @interface NSString (RACSequenceAdditions)
    
    @property (nonatomic, copy, readonly) RACSequence<NSString *> *rac_sequence;
    
    @end
    
    
    //属性的getter方法
    - (RACSequence *)rac_sequence {
        return [RACStringSequence sequenceWithString:self offset:0];
    }
    

    2-5. NSEnumerator+RACSequenceAdditions

    • 把任意一个NSEnumerator转换成RACSequence
      • 返回的RACSequence的head是当前的sequence的head
      • 返回的RACSequence的tail是当前的sequence本身
    @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;
        }];
    }
    

    2-6. NSIndexSet+RACSequenceAdditions

    把任意一个NSIndexSet转换成RACSequence

    @interface NSIndexSet (RACSequenceAdditions)
    
    @property (nonatomic, copy, readonly) RACSequence<NSNumber *> *rac_sequence;
    
    @end
    
    
    //底层实现
    - (RACSequence *)rac_sequence {
        return [RACIndexSetSequence sequenceWithIndexSet:self];
    }
    

    2-7. NSOrderedSet+RACSequenceAdditions

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

    @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;
    }
    

    3. 总结

    相关文章

      网友评论

          本文标题:ReactiveCocoa之集合使用详解02

          本文链接:https://www.haomeiwen.com/subject/dblnqftx.html