ReactiveCocoa 中 集合類RACSequence 和 RACTuple底層實現(xiàn)分析

前言

在OOP的世界里使用FRP的思想來編程秘案,光有函數(shù)這種一等公民答姥,還是無法滿足我們一些需求的漓糙。因此還是需要引用變量來完成各式各樣的類的操作行為锯玛。

在前幾篇文章中詳細的分析了RACStream中RACSignal的底層實現(xiàn)。RACStream還有另外一個子類兼蜈,RACSequence攘残,這個類是RAC專門為集合而設(shè)計的。這篇文章就專門分析一下RACSequence的底層實現(xiàn)为狸。

目錄

  • 1.RACTuple底層實現(xiàn)分析
  • 2.RACSequence底層實現(xiàn)分析
  • 3.RACSequence操作實現(xiàn)分析
  • 4.RACSequence的一些擴展

一. RACTuple底層實現(xiàn)分析

在分析RACSequence之前歼郭,先來看看RACTuple的實現(xiàn)。RACTuple是ReactiveCocoa的元組類辐棒。

1. RACTuple



@interface RACTuple : NSObject <NSCoding, NSCopying, NSFastEnumeration>

@property (nonatomic, readonly) NSUInteger count;

@property (nonatomic, readonly) id first;
@property (nonatomic, readonly) id second;
@property (nonatomic, readonly) id third;
@property (nonatomic, readonly) id fourth;
@property (nonatomic, readonly) id fifth;
@property (nonatomic, readonly) id last;
@property (nonatomic, strong) NSArray *backingArray;

@property (nonatomic, copy, readonly) RACSequence *rac_sequence; // 這個是專門為sequence提供的一個擴展

@end

RACTuple的定義看上去很簡單病曾,底層實質(zhì)就是一個NSArray,只不過封裝了一些方法漾根。RACTuple繼承了NSCoding, NSCopying, NSFastEnumeration這三個協(xié)議泰涂。


- (id)initWithCoder:(NSCoder *)coder {
    self = [self init];
    if (self == nil) return nil;
    
    self.backingArray = [coder decodeObjectForKey:@keypath(self.backingArray)];
    return self;
}

- (void)encodeWithCoder:(NSCoder *)coder {
    if (self.backingArray != nil) [coder encodeObject:self.backingArray forKey:@keypath(self.backingArray)];
}

這里是NSCoding協(xié)議。都是對內(nèi)部的backingArray進行decodeObjectForKey:和encodeObject: 辐怕。


- (instancetype)copyWithZone:(NSZone *)zone {
   // we're immutable, bitches!    <---這里是原作者的注釋
   return self;
}

上面這是NSCopying協(xié)議逼蒙。由于內(nèi)部是基于NSArray的,所以是immutable不可變的寄疏。


- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id __unsafe_unretained [])buffer count:(NSUInteger)len {
    return [self.backingArray countByEnumeratingWithState:state objects:buffer count:len];
}

上面是NSFastEnumeration協(xié)議是牢,快速枚舉也都是針對NSArray進行的操作僵井。


// 三個類方法
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array;
+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert;
+ (instancetype)tupleWithObjects:(id)object, ... NS_REQUIRES_NIL_TERMINATION;


- (id)objectAtIndex:(NSUInteger)index;
- (NSArray *)allObjects;
- (instancetype)tupleByAddingObject:(id)obj;

RACTuple的方法也不多,總共就6個方法驳棱,3個類方法批什,3個實例方法。

先看類方法:



+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array {
    return [self tupleWithObjectsFromArray:array convertNullsToNils:NO];
}

+ (instancetype)tupleWithObjectsFromArray:(NSArray *)array convertNullsToNils:(BOOL)convert {
    RACTuple *tuple = [[self alloc] init];
    
    if (convert) {
        NSMutableArray *newArray = [NSMutableArray arrayWithCapacity:array.count];
        for (id object in array) {
            [newArray addObject:(object == NSNull.null ? RACTupleNil.tupleNil : object)];
        }       
        tuple.backingArray = newArray;
    } else {
        tuple.backingArray = [array copy];
    }
    
    return tuple;
}

先看這兩個類方法社搅,這兩個類方法的區(qū)別在于是否把NSNull轉(zhuǎn)換成RACTupleNil類型驻债。根據(jù)入?yún)rray初始化RACTuple內(nèi)部的NSArray。

RACTuplePack( ) 和 RACTuplePack_( )這兩個宏的實現(xiàn)也是調(diào)用了tupleWithObjectsFromArray:方法


#define RACTuplePack(...) \
    RACTuplePack_(__VA_ARGS__)

#define RACTuplePack_(...) \
    ([RACTuple tupleWithObjectsFromArray:@[ metamacro_foreach(RACTuplePack_object_or_ractuplenil,, __VA_ARGS__) ]])


這里需要注意的是RACTupleNil


+ (RACTupleNil *)tupleNil {
    static dispatch_once_t onceToken;
    static RACTupleNil *tupleNil = nil;
    dispatch_once(&onceToken, ^{
        tupleNil = [[self alloc] init];
    });
    
    return tupleNil;
}

RACTupleNil是一個單例形葬。

重點需要解釋的是另外一種類方法:



+ (instancetype)tupleWithObjects:(id)object, ... {
    RACTuple *tuple = [[self alloc] init];
    
    va_list args;
    va_start(args, object);
    
    NSUInteger count = 0;
    for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) {
        ++count;
    }
    
    va_end(args);
    
    if (count == 0) {
        tuple.backingArray = @[];
        return tuple;
    }
    
    NSMutableArray *objects = [[NSMutableArray alloc] initWithCapacity:count];
    
    va_start(args, object);
    for (id currentObject = object; currentObject != nil; currentObject = va_arg(args, id)) {
        [objects addObject:currentObject];
    }
    
    va_end(args);
    
    tuple.backingArray = objects;
    return tuple;
}

這個類方法的參數(shù)是可變參數(shù)類型合呐。由于用到了可變參數(shù)類型,所以就會用到va_list荷并,va_start合砂,va_arg,va_end源织。


#ifndef _VA_LIST_T
#define _VA_LIST_T
typedef __darwin_va_list va_list;
#endif /* _VA_LIST_T */

#ifndef _VA_LIST
typedef __builtin_va_list va_list;
#define _VA_LIST
#endif
#define va_start(ap, param) __builtin_va_start(ap, param)
#define va_end(ap)          __builtin_va_end(ap)
#define va_arg(ap, type)    __builtin_va_arg(ap, type)

  1. va_list用于聲明一個變量翩伪,我們知道函數(shù)的可變參數(shù)列表其實就是一個字符串,所以va_list才被聲明為字符型指針谈息,這個類型用于聲明一個指向參數(shù)列表的字符型指針變量缘屹,例如:va_list ap;//ap:arguement pointer
  2. va_start(ap,v),它的第一個參數(shù)是指向可變參數(shù)字符串的變量,第二個參數(shù)是可變參數(shù)函數(shù)的第一個參數(shù)侠仇,通常用于指定可變參數(shù)列表中參數(shù)的個數(shù)轻姿。
  3. va_arg(ap,t),它的第一個參數(shù)指向可變參數(shù)字符串的變量,第二個參數(shù)是可變參數(shù)的類型逻炊。
  4. va_end(ap) 用于將存放可變參數(shù)字符串的變量清空(賦值為NULL)互亮。

剩下的3個實例方法都是對數(shù)組的操作,沒有什么難度余素。

一般使用用兩個宏豹休,RACTupleUnpack( ) 用來解包,RACTuplePack( ) 用來裝包桨吊。


   RACTupleUnpack(NSString *string, NSNumber *num) = [RACTuple tupleWithObjects:@"foo", @5, nil];

 
   RACTupleUnpack(NSString *string, NSNumber *num) = RACTuplePack(@"foo",@(5));

   NSLog(@"string: %@", string);
   NSLog(@"num: %@", num);

   /* 上面的做法等價于下面的 */
   RACTuple *t = [RACTuple tupleWithObjects:@"foo", @5, nil];
   NSString *string = t[0];
   NSNumber *num = t[1];
   NSLog(@"string: %@", string);
   NSLog(@"num: %@", num);


關(guān)于RACTuple還有2個相關(guān)的類威根,RACTupleUnpackingTrampoline,RACTupleSequence视乐。

2. RACTupleUnpackingTrampoline



@interface RACTupleUnpackingTrampoline : NSObject
+ (instancetype)trampoline;
- (void)setObject:(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;
}


RACTupleUnpackingTrampoline這個類也就只有一個作用,就是它對應(yīng)的實例方法佑淀。


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

這個方法里面會遍歷入?yún)?shù)組NSArray留美,然后依次取出數(shù)組里面每個value 的指針,用這個指針又賦值給了tuple[index]。

為了解釋清楚這個方法的作用独榴,寫出測試代碼:


    RACTupleUnpackingTrampoline *tramp = [RACTupleUnpackingTrampoline trampoline];
    
    NSString *string;
    NSString *string1;
    NSString *string2;
    
    NSArray *array = [NSArray arrayWithObjects:[NSValue valueWithPointer:&string],[NSValue valueWithPointer:&string1],[NSValue valueWithPointer:&string2], nil];
    
    NSLog(@"調(diào)用方法之前 string = %@,string1 = %@,string2 = %@",string,string1,string2);
    
    [tramp setObject:[RACTuple tupleWithObjectsFromArray:@[(@"foo"),(@(10)),@"32323"]] forKeyedSubscript:array];
    
    NSLog(@"調(diào)用方法之后 string = %@,string1 = %@,string2 = %@",string,string1,string2);


輸出如下:


調(diào)用方法之前 string = (null),string1 = (null),string2 = (null)
調(diào)用方法之后 string = foo,string1 = 10,string2 = 32323


這個函數(shù)的作用也就一清二楚了僧叉。但是平時我們是很少用到[NSValue valueWithPointer:&string]這種寫法的奕枝。究竟是什么地方會用到這個函數(shù)呢棺榔?全局搜索一下,找到了用到這個的地方隘道。

在RACTuple 中兩個非常有用的宏:RACTupleUnpack( ) 用來解包症歇,RACTuplePack( ) 用來裝包。RACTuplePack( )的實現(xiàn)在上面分析過了谭梗,實際是調(diào)用tupleWithObjectsFromArray:方法忘晤。那么RACTupleUnpack( ) 的宏是怎么實現(xiàn)的呢?這里就用到了RACTupleUnpackingTrampoline激捏。


#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__) ] ]


以上就是RACTupleUnpack( ) 具體的宏设塔。看上去很復(fù)雜远舅。還是寫出測試代碼分析分析闰蛔。


    RACTupleUnpack(NSString *string, NSNumber *num) = RACTuplePack(@"foo",@(10));


把上述的代碼編譯之后的代碼貼出來:


    __attribute__((objc_ownership(strong))) id RACTupleUnpack284_var0;
    __attribute__((objc_ownership(strong))) id RACTupleUnpack284_var1;
    
    int RACTupleUnpack_state284 = 0;
    RACTupleUnpack_after284: ;
    __attribute__((objc_ownership(strong))) NSString *string = RACTupleUnpack284_var0;
    __attribute__((objc_ownership(strong))) NSNumber *num = RACTupleUnpack284_var1;
    
    if (RACTupleUnpack_state284 != 0)
        RACTupleUnpack_state284 = 2;
    
    while (RACTupleUnpack_state284 != 2)
        if (RACTupleUnpack_state284 == 1) {
            goto RACTupleUnpack_after284;
        } else for (; RACTupleUnpack_state284 != 1; RACTupleUnpack_state284 = 1)
            [RACTupleUnpackingTrampoline trampoline][ @[ [NSValue valueWithPointer:&RACTupleUnpack284_var0], [NSValue valueWithPointer:&RACTupleUnpack284_var1], ] ] = ([RACTuple tupleWithObjectsFromArray:@[ (@"foo") ?: RACTupleNil.tupleNil, (@(10)) ?: RACTupleNil.tupleNil, ]]);

轉(zhuǎn)換成這樣就比較好理解了。RACTupleUnpack_after284: 是一個標號图柏。RACTupleUnpack_state284初始值為0序六,在下面while里面有一個for循環(huán),在這個循環(huán)里面會進行解包操作蚤吹,也就是會調(diào)用setObject:forKeyedSubscript:函數(shù)例诀。

在循環(huán)里面,


[RACTupleUnpackingTrampoline trampoline][ @[ [NSValue valueWithPointer:&RACTupleUnpack284_var0], [NSValue valueWithPointer:&RACTupleUnpack284_var1], ] ]


這里就是調(diào)用了[NSValue valueWithPointer:&string]的寫法裁着。

至此繁涂,RACTupleUnpackingTrampoline這個類的作用也已明了,它是被作用設(shè)計出來用來實現(xiàn)神奇的RACTupleUnpack( ) 這個宏二驰。

當(dāng)然RACTupleUnpackingTrampoline這個類的setObject:forKeyedSubscript:函數(shù)也可以使用扔罪,只不過要注意寫法,注意指針的類型诸蚕,在NSValue里面包裹的是valueWithPointer步势,(nullable const void *)pointer類型的。

3. RACTupleSequence

這個類僅僅只是名字里面帶有Tuple而已背犯,它其實是繼承自RACSequence坏瘩。

需要分析這個類的原因是因為RACTuple里面有一個拓展的屬性rac_sequence。


- (RACSequence *)rac_sequence {
   return [RACTupleSequence sequenceWithTupleBackingArray:self.backingArray offset:0];
}

還是先看看RACTupleSequence的定義漠魏。


@interface RACTupleSequence : RACSequence
@property (nonatomic, strong, readonly) NSArray *tupleBackingArray;
@property (nonatomic, assign, readonly) NSUInteger offset;
+ (instancetype)sequenceWithTupleBackingArray:(NSArray *)backingArray offset:(NSUInteger)offset;
@end

這個類是繼承自RACSequence倔矾,而且只有這一個類方法。

tupleBackingArray是來自于RACTuple里面的backingArray。


+ (instancetype)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轉(zhuǎn)換成Sequence哪自。Sequence里面的數(shù)組就是Tuple內(nèi)部的backingArray丰包。offset從0開始。

二. RACSequence底層實現(xiàn)分析


@interface RACSequence : RACStream <NSCoding, NSCopying, NSFastEnumeration>

@property (nonatomic, strong, readonly) id head;
@property (nonatomic, strong, readonly) RACSequence *tail;
@property (nonatomic, copy, readonly) NSArray *array;
@property (nonatomic, copy, readonly) NSEnumerator *objectEnumerator;
@property (nonatomic, copy, readonly) RACSequence *eagerSequence;
@property (nonatomic, copy, readonly) RACSequence *lazySequence;
@end

RACSequence是RACStream的子類壤巷,主要是ReactiveCocoa里面的集合類邑彪。

先來說說關(guān)于RACSequence的一些概念。

RACSequence有兩個很重要的屬性就是head和tail胧华。head是一個id寄症,而tail又是一個RACSequence,這個定義有點遞歸的意味矩动。


    RACSequence *sequence = [RACSequence sequenceWithHeadBlock:^id{
        return @(1);
    } tailBlock:^RACSequence *{
        return @[@2,@3,@4].rac_sequence;
    }];
    
    NSLog(@"sequence.head = %@ , sequence.tail =  %@",sequence.head ,sequence.tail);


輸出:


sequence.head = 1 , sequence.tail =  <RACArraySequence: 0x608000223920>{ name = , array = (
    2,
    3,
    4
) }


這段測試代碼就道出了head和tail的定義有巧。更加詳細的描述見下圖:

上述代碼里面用到了RACSequence初始化的方法,具體的分析見后面悲没。

objectEnumerator是一個快速枚舉器篮迎。



@interface RACSequenceEnumerator : NSEnumerator
@property (nonatomic, strong) RACSequence *sequence;
@end

之所以需要實現(xiàn)這個,是為了更加方便的RACSequence進行遍歷示姿。


- (id)nextObject {
    id object = nil;
    
    @synchronized (self) {
        object = self.sequence.head;
        self.sequence = self.sequence.tail;
    }
    
    return object;
}

有了這個NSEnumerator甜橱,就可以從RACSequence的head一直遍歷到tail。


- (NSEnumerator *)objectEnumerator {
    RACSequenceEnumerator *enumerator = [[RACSequenceEnumerator alloc] init];
    enumerator.sequence = self;
    return enumerator;
}

回到RACSequence的定義里面的objectEnumerator峻凫,這里就是取出內(nèi)部的RACSequenceEnumerator渗鬼。


- (NSArray *)array {
    NSMutableArray *array = [NSMutableArray array];
    for (id obj in self) {
        [array addObject:obj];
    }   
    return [array copy];
}

RACSequence的定義里面還有一個array,這個數(shù)組就是返回一個NSArray荧琼,這個數(shù)組里面裝滿了RACSequence里面所有的對象譬胎。這里之所以能用for-in,是因為實現(xiàn)了NSFastEnumeration協(xié)議命锄。至于for-in的效率堰乔,完全就看重寫NSFastEnumeration協(xié)議里面countByEnumeratingWithState: objects: count: 方法里面的執(zhí)行效率了。

在分析RACSequence的for-in執(zhí)行效率之前脐恩,先回顧一下NSFastEnumerationState的定義镐侯,這里的屬性在接下來的實現(xiàn)中會被大量使用。


typedef struct {
    unsigned long state; //可以被自定義成任何有意義的變量
    id __unsafe_unretained _Nullable * _Nullable itemsPtr;  //返回對象數(shù)組的首地址
    unsigned long * _Nullable mutationsPtr;  //指向會隨著集合變動而變化的一個值
    unsigned long extra[5]; //可以被自定義成任何有意義的數(shù)組
} NSFastEnumerationState;

接下來要分析的這個函數(shù)的入?yún)⑹幻埃瑂tackbuf是為for-in提供的對象數(shù)組苟翻,len是該數(shù)組的長度。


- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id *)stackbuf count:(NSUInteger)len {
    // 定義完成時候的狀態(tài)為state = ULONG_MAX
    if (state->state == ULONG_MAX) {
        return 0;
    }
    
    // 由于我們需要遍歷sequence多次骗污,所以這里定義state字段來記錄sequence的首地址
    RACSequence *(^getSequence)(void) = ^{
        return (__bridge RACSequence *)(void *)state->state;
    };
    
    void (^setSequence)(RACSequence *) = ^(RACSequence *sequence) {
        // 釋放老的sequence
        CFBridgingRelease((void *)state->state);
        // 保留新的sequence崇猫,把sequence的首地址存放入state中
        state->state = (unsigned long)CFBridgingRetain(sequence);
    };
    
    void (^complete)(void) = ^{
        // 釋放sequence,并把state置為完成態(tài)
        setSequence(nil);
        state->state = ULONG_MAX;
    };
    
    // state == 0是第一次調(diào)用時候的初始值
    if (state->state == 0) {
        // 在遍歷過程中需忿,如果Sequence不再發(fā)生變化诅炉,那么就讓mutationsPtr指向一個定值蜡歹,指向extra數(shù)組的首地址
        state->mutationsPtr = state->extra;
        // 再次刷新state的值
        setSequence(self);
    }
    
    // 將會把返回的對象放進stackbuf中,因此用itemsPtr指向它
    state->itemsPtr = stackbuf;
    
    NSUInteger enumeratedCount = 0;
    while (enumeratedCount < len) {
        RACSequence *seq = getSequence();
        // 由于sequence可能是懶加載生成的涕烧,所以需要防止在遍歷器enumerator遍歷到它們的時候被釋放了

        __autoreleasing id obj = seq.head;

        // 沒有頭就結(jié)束遍歷
        if (obj == nil) {
            complete();
            break;
        }
        // 遍歷sequence月而,每次取出來的head都放入stackbuf數(shù)組中。
        stackbuf[enumeratedCount++] = obj;
        
        // 沒有尾就是完成遍歷
        if (seq.tail == nil) {
            complete();
            break;
        }
        
        // 取出tail以后议纯,這次遍歷結(jié)束的tail父款,即為下次遍歷的head,設(shè)置seq.tail為Sequence的head痹扇,為下次循環(huán)做準備
        setSequence(seq.tail);
    }
    
    return enumeratedCount;
}


整個遍歷的過程類似遞歸的過程铛漓,從頭到尾依次遍歷一遍溯香。

再來研究研究RACSequence的初始化:


+ (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence *(^)(void))tailBlock;

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

初始化RACSequence鲫构,會調(diào)用RACDynamicSequence。這里有點類比RACSignal的RACDynamicSignal玫坛。

再來看看RACDynamicSequence的定義结笨。


@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


這里需要說明的是此處的headBlock,tailBlock湿镀,dependencyBlock的修飾符都是用了strong炕吸,而不是copy。這里是一個很奇怪的bug導(dǎo)致的勉痴。在https://github.com/ReactiveCocoa/ReactiveCocoa/issues/505中詳細記錄了用copy關(guān)鍵字會導(dǎo)致內(nèi)存泄露的bug赫模。具體代碼如下:


[[[@[@1,@2,@3,@4,@5] rac_sequence] filter:^BOOL(id value) {
    return [value intValue] > 1;
}] array];

最終發(fā)現(xiàn)這個問題的人把copy改成strong就神奇的修復(fù)了這個bug。最終整個ReactiveCocoa庫里面就只有這里把block的關(guān)鍵字從copy改成了strong蒸矛,而不是所有的地方都改成strong瀑罗。

原作者Justin Spahr-Summers大神對這個問題的最終解釋是:

Maybe there's just something weird with how we override dealloc, set the blocks from a class method, cast them, or something else.

所以日常我們寫block的時候,沒有特殊情況雏掠,依舊需要繼續(xù)用copy進行修飾斩祭。


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

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

hasDependency這個變量是代表是否有dependencyBlock。這個函數(shù)里面就只把headBlock和tailBlock保存起來了乡话。


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

另外一個類方法sequenceWithLazyDependency: headBlock: tailBlock:是帶有dependencyBlock的摧玫,這個方法里面會保存headBlock,tailBlock绑青,dependencyBlock這3個block诬像。

從RACSequence這兩個唯一的初始化方法之間就引出了RACSequence兩大核心問題之一,積極運算 和 惰性求值闸婴。

1. 積極運算 和 惰性求值

在RACSequence的定義中還有兩個RACSequence —— eagerSequence 和 lazySequence坏挠。這兩個RACSequence就是分別對應(yīng)著積極運算的RACSequence和惰性求值的RACSequence。

關(guān)于這兩個概念最最新形象的比喻還是臧老師博客里面的這篇文章聊一聊iOS開發(fā)中的惰性計算里面寫的一段笑話掠拳。引入如下:

有一只小白兔癞揉,跑到蔬菜店里問老板:“老板,有100個胡蘿卜嗎?”喊熟。老板說:“沒有那么多啊柏肪。”芥牌,小白兔失望的說道:“哎烦味,連100個胡蘿卜都沒有。壁拉。谬俄。”弃理。第二天小白兔又來到蔬菜店問老板:“今天有100個胡蘿卜了吧溃论?”,老板尷尬的說:“今天還是缺點痘昌,明天就能好了钥勋。”辆苔,小白兔又很失望的走了算灸。第三天小白兔剛一推門,老板就高興的說道:“有了有了驻啤,從前天就進貨的100個胡蘿卜到貨了菲驴。”骑冗,小白兔說:“太好了赊瞬,我要買2根!”沐旨。森逮。。

如果日常我們遇到了這種問題磁携,就很浪費內(nèi)存空間了褒侧。比如在內(nèi)存里面開了一個100W大小的數(shù)組,結(jié)果實際只使用到100個數(shù)值谊迄。這個時候就需要用到惰性運算了闷供。

在RACSequence里面這兩種方式都支持,我們來看看底層源碼是如何實現(xiàn)的统诺。

先來看看平時我們很熟悉的情況——積極運算歪脏。

在RACSequence中積極運算的代表是RACSequence的一個子類RACArraySequence的子類——RACEagerSequence。它的積極運算表現(xiàn)在其bind函數(shù)上粮呢。


- (instancetype)bind:(RACStreamBindBlock (^)(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];
}




從上述代碼中能看到主要是進行了2層循環(huán)婿失,最外層循環(huán)遍歷的自己RACSequence中的值钞艇,然后拿到這個值傳入閉包bindBlock( )中,返回一個RACSequence豪硅,最后用一個NSMutableArray依次把每個RACSequence里面的值都裝起來哩照。

第二個for-in循環(huán)是在遍歷RACSequence,之所以可以用for-in的方式遍歷就是因為實現(xiàn)了NSFastEnumeration協(xié)議懒浮,實現(xiàn)了countByEnumeratingWithState: objects: count: 方法飘弧,這個方法在上面詳細分析過了,這里不再贅述砚著。

這里就是一個積極運算的例子次伶,在每次循環(huán)中都會把閉包block( )的值計算出來。值得說明的是稽穆,最后返回的RACSequence的類型是self.class類型的冠王,即還是RACEagerSequence類型的。

再來看看RACSequence中的惰性求值是怎么實現(xiàn)的秧骑。

在RACSequence中版确,bind函數(shù)是下面這個樣子:


- (instancetype)bind:(RACStreamBindBlock (^)(void))block {
    RACStreamBindBlock bindBlock = block();
    return [[self bind:bindBlock passingThroughValuesFromSequence:nil] setNameWithFormat:@"[%@] -bind:", self.name];
}

實際上調(diào)用了bind: passingThroughValuesFromSequence:方法,第二個入?yún)魅雗il乎折。


- (instancetype)bind:(RACStreamBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence {

    __block RACSequence *valuesSeq = self;
    __block RACSequence *current = passthroughSequence;
    __block BOOL stop = NO;
    
    RACSequence *sequence = [RACDynamicSequence sequenceWithLazyDependency:^ id {
        // 暫時省略
    } 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: passingThroughValuesFromSequence:方法的實現(xiàn)中,就是用sequenceWithLazyDependency: headBlock: tailBlock:方法生成了一個RACSequence侵歇,并返回骂澄。在sequenceWithLazyDependency: headBlock: tailBlock:上面分析過源碼,主要目的是為了保存3個閉包惕虑,headBlock坟冲,tailBlock,dependencyBlock溃蔫。

通過調(diào)用RACSequence里面的bind操作健提,并沒有執(zhí)行3個閉包里面的值,只是保存起來了伟叛。這里就是惰性求值的表現(xiàn)——等到要用的時候才會計算私痹。

通過上述源碼的分析,可以寫出如下的測試代碼加深理解统刮。



    NSArray *array = @[@1,@2,@3,@4,@5];
    
    RACSequence *lazySequence = [array.rac_sequence map:^id(id value) {
        NSLog(@"lazySequence");
        return @(101);
    }];
    
    RACSequence *eagerSequence = [array.rac_sequence.eagerSequence map:^id(id value) {
        NSLog(@"eagerSequence");
        return @(100);
    }];


上述代碼運行之后紊遵,會輸出如下信息:


eagerSequence
eagerSequence
eagerSequence
eagerSequence
eagerSequence

只輸出了5遍eagerSequence,lazySequence并沒有輸出侥蒙。原因是因為bind閉包只在eagerSequence中真正被調(diào)用執(zhí)行了暗膜,而在lazySequence中bind閉包僅僅只是被copy了。

那如何讓lazySequence執(zhí)行bind閉包呢鞭衩?


    [lazySequence array];

通過執(zhí)行上述代碼学搜,就可以輸出5遍“l(fā)azySequence”了娃善。因為bind閉包再次會被調(diào)用執(zhí)行。

積極運算 和 惰性求值在這里就區(qū)分出來了瑞佩。在RACSequence中会放,除去RACEagerSequence只積極運算,其他的Sequence都是惰性求值的钉凌。

接下來再繼續(xù)分析RACSequence是如何實現(xiàn)惰性求值的咧最。


RACSequence *sequence = [RACDynamicSequence sequenceWithLazyDependency:^ id {
    while (current.head == nil) {
        if (stop) return nil;
        
        // 遍歷當(dāng)前sequence,取出下一個值
        id value = valuesSeq.head;
        
        if (value == nil) {
            // 遍歷完sequence所有的值
            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];
}];

在bind操作中創(chuàng)建了這樣一個lazySequence御雕,3個block閉包保存了如何創(chuàng)建一個lazySequence的做法矢沿。

headBlock是入?yún)閕d,返回值也是一個id酸纲。在創(chuàng)建lazySequence的head的時候捣鲸,并不關(guān)心入?yún)ⅲ苯臃祷豴assthroughSequence的head闽坡。

tailBlock是入?yún)閕d栽惶,返回值為RACSequence。由于RACSequence的定義類似遞歸定義的疾嗅,所以tailBlock會再次遞歸調(diào)用bind:passingThroughValuesFromSequence:產(chǎn)生一個RACSequence作為新的sequence的tail外厂。

dependencyBlock的返回值是作為headBlock和tailBlock的入?yún)ⅰ2贿^現(xiàn)在headBlock和tailBlock都不關(guān)心這個入?yún)⒋小D敲磀ependencyBlock就是成為了headBlock和tailBlock閉包執(zhí)行之前要執(zhí)行的閉包汁蝶。

dependencyBlock的目的是為了把原來的sequence里面的值,都進行一次變換论悴。current是入?yún)assthroughSequence掖棉,valuesSeq就是原sequence的引用。每次循環(huán)一次就取出原sequence的頭膀估,直到取不到為止幔亥,就是遍歷完成。

取出valuesSeq的head察纯,傳入bindBlock( )閉包進行變換帕棉,返回值是一個current 的sequence。在每次headBlock和tailBlock之前都會調(diào)用這個dependencyBlock捐寥,變換后新的sequence的head就是current的head笤昨,新的sequence的tail就是遞歸調(diào)用傳入的current.tail。

RACDynamicSequence創(chuàng)建的lazyDependency的過程就是保存了3個block的過程。那這些閉包什么時候會被調(diào)用呢?


- (id)head {
    @synchronized (self) {
        id untypedHeadBlock = self.headBlock;
        if (untypedHeadBlock == nil) return _head;
        
        if (self.hasDependency) {
            if (self.dependencyBlock != nil) {
                _dependency = self.dependencyBlock();
                self.dependencyBlock = nil;
            }
            
            id (^headBlock)(id) = untypedHeadBlock;
            _head = headBlock(_dependency);
        } else {
            id (^headBlock)(void) = untypedHeadBlock;
            _head = headBlock();
        }
        
        self.headBlock = nil;
        return _head;
    }
}


上面的源碼就是獲取RACDynamicSequence中head的實現(xiàn)沙合。當(dāng)要取出sequence的head的時候横辆,就會調(diào)用headBlock( )邮旷。如果保存了dependencyBlock閉包哀澈,在執(zhí)行headBlock( )之前會先執(zhí)行dependencyBlock( )進行一次變換掠抬。


- (RACSequence *)tail {
    @synchronized (self) {
        id untypedTailBlock = self.tailBlock;
        if (untypedTailBlock == nil) return _tail;
        
        if (self.hasDependency) {
            if (self.dependencyBlock != nil) {
                _dependency = self.dependencyBlock();
                self.dependencyBlock = nil;
            }
            
            RACSequence * (^tailBlock)(id) = untypedTailBlock;
            _tail = tailBlock(_dependency);
        } else {
            RACSequence * (^tailBlock)(void) = untypedTailBlock;
            _tail = tailBlock();
        }
        
        if (_tail.name == nil) _tail.name = self.name;
        
        self.tailBlock = nil;
        return _tail;
    }
}


獲取RACDynamicSequence中tail的時候骄崩,和獲取head是一樣的拔稳,當(dāng)需要取出tail的時候才會調(diào)用tailBlock( )葛峻。當(dāng)有dependencyBlock閉包,會先執(zhí)行dependencyBlock閉包巴比,再調(diào)用tailBlock( )术奖。

總結(jié)一下:

  1. RACSequence的惰性求值,除去RACEagerSequence的bind函數(shù)以外轻绞,其他所有的Sequence都是基于惰性求值的采记。只有到取出來運算之前才會去把相應(yīng)的閉包執(zhí)行一遍。

  2. 在RACSequence所有函數(shù)中政勃,只有bind函數(shù)會傳入dependencyBlock( )閉包唧龄,(RACEagerSequence會重寫這個bind函數(shù)),所以看到dependencyBlock( )閉包一定可以推斷出是RACSequence做了變換操作了奸远。

2. Pull-driver 和 Push-driver

在RACSequence中有一個方法可以讓RACSequence和RACSignal進行關(guān)聯(lián)上既棺。



- (RACSignal *)signal {
    return [[self signalWithScheduler:[RACScheduler scheduler]] setNameWithFormat:@"[%@] -signal", self.name];
}

- (RACSignal *)signalWithScheduler:(RACScheduler *)scheduler {
    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        __block RACSequence *sequence = self;
        
        return [scheduler scheduleRecursiveBlock:^(void (^reschedule)(void)) {
            if (sequence.head == nil) {
                [subscriber sendCompleted];
                return;
            }            
            [subscriber sendNext:sequence.head];           
            sequence = sequence.tail;
            reschedule();
        }];
    }] setNameWithFormat:@"[%@] -signalWithScheduler: %@", self.name, scheduler];
}

RACSequence中的signal方法會調(diào)用signalWithScheduler:方法。在signalWithScheduler:方法中會創(chuàng)建一個新的信號懒叛。這個新的信號的RACDisposable信號由scheduleRecursiveBlock:產(chǎn)生丸冕。



- (void)scheduleRecursiveBlock:(RACSchedulerRecursiveBlock)recursiveBlock addingToDisposable:(RACCompoundDisposable *)disposable {
    @autoreleasepool {
        RACCompoundDisposable *selfDisposable = [RACCompoundDisposable compoundDisposable];
        [disposable addDisposable:selfDisposable];
        
        __weak RACDisposable *weakSelfDisposable = selfDisposable;
        
        RACDisposable *schedulingDisposable = [self schedule:^{
     
            if (disposable.disposed) return;
            
            void (^reallyReschedule)(void) = ^{
                if (disposable.disposed) return;          
                // 這里是遞歸
                [self scheduleRecursiveBlock:recursiveBlock addingToDisposable:disposable];
            };
            
            // 這里實際上不需要__block關(guān)鍵字,但是由于Clang編譯器的特性芍瑞,為了保護下面的變量晨仑,所以加上了__block關(guān)鍵字
            __block NSLock *lock = [[NSLock alloc] init];
            lock.name = [NSString stringWithFormat:@"%@ %s", self, sel_getName(_cmd)];
            
            __block NSUInteger rescheduleCount = 0;
            
            // 一旦同步操作執(zhí)行完成,rescheduleImmediately就應(yīng)該被設(shè)為YES
            __block BOOL rescheduleImmediately = NO;
            
            @autoreleasepool {
                recursiveBlock(^{
                    [lock lock];
                    BOOL immediate = rescheduleImmediately;
                    if (!immediate) ++rescheduleCount;
                    [lock unlock];
                    
                    if (immediate) reallyReschedule();
                });
            }
            
            [lock lock];
            NSUInteger synchronousCount = rescheduleCount;
            rescheduleImmediately = YES;
            [lock unlock];
            
            for (NSUInteger i = 0; i < synchronousCount; i++) {
                reallyReschedule();
            }
        }];
        
        [selfDisposable addDisposable:schedulingDisposable];
    }
}

這段代碼雖然長拆檬,但是拆分分析一下:


__block NSUInteger rescheduleCount = 0; 

// 一旦同步操作執(zhí)行完成,rescheduleImmediately就應(yīng)該被設(shè)為YES 
__block BOOL rescheduleImmediately = NO;

rescheduleCount 是遞歸次數(shù)計數(shù)妥凳。rescheduleImmediately這個BOOL是決定是否立即執(zhí)行reallyReschedule( )閉包竟贯。

recursiveBlock是入?yún)ⅲ鼘嶋H是下面這段閉包代碼:


{
   if (sequence.head == nil) {
    [subscriber sendCompleted];
    return;
   }

   [subscriber sendNext:sequence.head];

   sequence = sequence.tail;
   reschedule();
  }

recursiveBlock的入?yún)⑹莚eschedule( )逝钥。執(zhí)行完上面的代碼之后開始執(zhí)行入?yún)eschedule( )的代碼屑那,入?yún)eschedule( 閉包的代碼是如下:


    ^{
            [lock lock];
            BOOL immediate = rescheduleImmediately;
            if (!immediate) ++rescheduleCount;
            [lock unlock];
                    
            if (immediate) reallyReschedule();
    }

在這段block中會統(tǒng)計rescheduleCount,如果rescheduleImmediately為YES還會繼續(xù)開始執(zhí)行遞歸操作reallyReschedule( )艘款。


   for (NSUInteger i = 0; i < synchronousCount; i++) {
    reallyReschedule();
   }

最終會在這個循環(huán)里面遞歸調(diào)用reallyReschedule( )閉包持际。reallyReschedule( )閉包執(zhí)行的操作就是再次執(zhí)行scheduleRecursiveBlock:recursiveBlock addingToDisposable:disposable方法。

每次執(zhí)行一次遞歸就會取出sequence的head值發(fā)送出來哗咆,直到sequence.head = = nil發(fā)送完成信號蜘欲。

既然RACSequence也可以轉(zhuǎn)換成RACSignal,那么就需要總結(jié)一下兩者的異同點晌柬。

總結(jié)一下:

RACSequence 和 RACSignal 異同點對比:

  1. RACSequence除去RACEagerSequence姥份,其他所有的都是基于惰性計算的郭脂,這和RACSignal是一樣的。
  2. RACSequence是在時間上是連續(xù)的澈歉,一旦把RACSequence變成signal展鸡,再訂閱,會立即把所有的值一口氣都發(fā)送出來埃难。RACSignal是在時間上是離散的莹弊,當(dāng)有事件到來的時候,才會發(fā)送出數(shù)據(jù)流涡尘。
  3. RACSequence是Pull-driver忍弛,由訂閱者來決定是否發(fā)送值,只要訂閱者訂閱了悟衩,就會發(fā)送數(shù)據(jù)流剧罩。RACSignal是Push-driver,它發(fā)送數(shù)據(jù)流是不由訂閱者決定的座泳,不管有沒有訂閱者惠昔,它有離散事件產(chǎn)生了,就會發(fā)送數(shù)據(jù)流挑势。
  4. RACSequence發(fā)送的全是數(shù)據(jù)镇防,RACSignal發(fā)送的全是事件。事件不僅僅包括數(shù)據(jù)潮饱,還包括事件的狀態(tài)来氧,比如說事件是否出錯,事件是否完成香拉。

三. RACSequence操作實現(xiàn)分析

RACSequence還有以下幾個操作啦扬。


- (id)foldLeftWithStart:(id)start reduce:(id (^)(id accumulator, id value))reduce;
- (id)foldRightWithStart:(id)start reduce:(id (^)(id first, RACSequence *rest))reduce;
- (BOOL)any:(BOOL (^)(id value))block;
- (BOOL)all:(BOOL (^)(id value))block;
- (id)objectPassingTest:(BOOL (^)(id value))block;

1. foldLeftWithStart: 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;
}


這個函數(shù)傳入了一個初始值start,然后依次循環(huán)執(zhí)行reduce( )凫碌,循環(huán)之后扑毡,最終的值作為返回值返回。這個函數(shù)就是折疊函數(shù)盛险,從左邊折疊到右邊瞄摊。

2. foldRightWithStart: reduce:



- (id)foldRightWithStart:(id)start reduce:(id (^)(id, RACSequence *))reduce {
    NSCParameterAssert(reduce != NULL);
    
    if (self.head == nil) return start;
    
    RACSequence *rest = [RACSequence sequenceWithHeadBlock:^{
        return [self.tail foldRightWithStart:start reduce:reduce];
    } tailBlock:nil];
    
    return reduce(self.head, rest);
}


這個函數(shù)和上一個foldLeftWithStart: reduce:是一樣的,只不過方向是從右往左苦掘。

3. objectPassingTest:


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

    return [self filter:block].head;
}

objectPassingTest:里面會調(diào)用RACStream中的filter:函數(shù)换帜,這個函數(shù)在前幾篇文章分析過了。如果block(value)為YES鹤啡,就代表通過了Test惯驼,那么就會返回value的sequence。取出head返回揉忘。

4. any:


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

any:會調(diào)用objectPassingTest:函數(shù)跳座,如果不為nil就代表有value值通過了Test端铛,有通過了value的就返回YES,反之返回NO疲眷。

5. all:


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

all:會從左往右依次對每個值進行block( ) Test禾蚕,然后每個值依次進行&&操作。

6. concat:


- (instancetype)concat:(RACStream *)stream {
    NSCParameterAssert(stream != nil);
    
    return [[[RACArraySequence sequenceWithArray:@[ self, stream ] offset:0]
             flatten]
            setNameWithFormat:@"[%@] -concat: %@", self.name, stream];
}


concat:的操作和RACSignal的作用是一樣的狂丝。它會把原sequence和入?yún)tream連接到一起换淆,組合成一個高階sequence,最后調(diào)用flatten“拍扁”几颜。關(guān)于flatten的實現(xiàn)見前幾篇RACStream里面的flatten實現(xiàn)分析倍试。

7. zipWith:


- (instancetype)zipWith:(RACSequence *)sequence {
    NSCParameterAssert(sequence != nil);
    
    return [[RACSequence
             sequenceWithHeadBlock:^ id {
                 if (self.head == nil || sequence.head == nil) return nil;
                 return RACTuplePack(self.head, sequence.head);
             } tailBlock:^ id {
                 if (self.tail == nil || [[RACSequence empty] isEqual:self.tail]) return nil;
                 if (sequence.tail == nil || [[RACSequence empty] isEqual:sequence.tail]) return nil;
                 
                 return [self.tail zipWith:sequence.tail];
             }]
            setNameWithFormat:@"[%@] -zipWith: %@", self.name, sequence];
}

由于sequence的定義是遞歸形式的,所以zipWith:也是遞歸來進行的蛋哭。zipWith:新的sequence的head是原來2個sequence的head組合成RACTuplePack县习。新的sequence的tail是原來2個sequence的tail遞歸調(diào)用zipWith:。

四. RACSequence的一些擴展

關(guān)于RACSequence有以下9個子類谆趾,其中RACEagerSequence是繼承自RACArraySequence躁愿。這些子類看名字就知道sequence里面裝的是什么類型的數(shù)據(jù)。RACUnarySequence里面裝的是單元sequence沪蓬。它只有head值彤钟,沒有tail值。

RACSequenceAdditions 總共有7個Category跷叉。這7個Category分別對iOS 里面的集合類進行了RACSequence的擴展逸雹,使我們能更加方便的使用RACSequence。

1. NSArray+RACSequenceAdditions


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

這個Category能把任意一個NSArray數(shù)組轉(zhuǎn)換成RACSequence云挟。


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

根據(jù)NSArray創(chuàng)建一個RACArraySequence并返回梆砸。

2. NSDictionary+RACSequenceAdditions


@interface NSDictionary (RACSequenceAdditions)
@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
@property (nonatomic, copy, readonly) RACSequence *rac_keySequence;
@property (nonatomic, copy, readonly) RACSequence *rac_valueSequence;
@end

這個Category能把任意一個NSDictionary字典轉(zhuǎn)換成RACSequence。



- (RACSequence *)rac_sequence {
   NSDictionary *immutableDict = [self copy];
     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會把字典都轉(zhuǎn)化為一個裝滿RACTuplePack的RACSequence园欣,在這個RACSequence中辫樱,第一個位置是key,第二個位置是value俊庇。

rac_keySequence是裝滿所有key的RACSequence。

rac_valueSequence是裝滿所有value的RACSequence鸡挠。

3. NSEnumerator+RACSequenceAdditions


@interface NSEnumerator (RACSequenceAdditions)
@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
@end


這個Category能把任意一個NSEnumerator轉(zhuǎn)換成RACSequence辉饱。



- (RACSequence *)rac_sequence {
    return [RACSequence sequenceWithHeadBlock:^{
        return [self nextObject];
    } tailBlock:^{
        return self.rac_sequence;
    }];
}

返回的RACSequence的head是當(dāng)前的sequence的head,tail就是當(dāng)前的sequence拣展。

4. NSIndexSet+RACSequenceAdditions


@interface NSIndexSet (RACSequenceAdditions)
@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
@end


這個Category能把任意一個NSIndexSet轉(zhuǎn)換成RACSequence彭沼。



- (RACSequence *)rac_sequence {
    return [RACIndexSetSequence sequenceWithIndexSet:self];
}

+ (instancetype)sequenceWithIndexSet:(NSIndexSet *)indexSet {
    NSUInteger count = indexSet.count;
    if (count == 0) return self.empty;
    NSUInteger sizeInBytes = sizeof(NSUInteger) * count;
    NSMutableData *data = [[NSMutableData alloc] initWithCapacity:sizeInBytes];
    [indexSet getIndexes:data.mutableBytes maxCount:count inIndexRange:NULL];
    
    RACIndexSetSequence *seq = [[self alloc] init];
    seq->_data = data;
    seq->_indexes = data.bytes;
    seq->_count = count;
    return seq;
}


返回RACIndexSetSequence,在這個IndexSetSequence中备埃,data里面裝的NSData姓惑,indexes里面裝的NSUInteger褐奴,count里面裝的是index的總數(shù)。

5. NSOrderedSet+RACSequenceAdditions


@interface NSOrderedSet (RACSequenceAdditions)
@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
@end

這個Category能把任意一個NSOrderedSet轉(zhuǎn)換成RACSequence于毙。


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

返回的NSOrderedSet中的數(shù)組轉(zhuǎn)換成sequence敦冬。

6. NSSet+RACSequenceAdditions


@interface NSSet (RACSequenceAdditions)
@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
@end

這個Category能把任意一個NSSet轉(zhuǎn)換成RACSequence。


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

根據(jù)NSSet的allObjects數(shù)組創(chuàng)建一個RACArraySequence并返回唯沮。

7. NSString+RACSequenceAdditions


@interface NSString (RACSequenceAdditions)
@property (nonatomic, copy, readonly) RACSequence *rac_sequence;
@end

這個Category能把任意一個NSString轉(zhuǎn)換成RACSequence脖旱。


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


返回的是一個裝滿string字符的數(shù)組對應(yīng)的sequence。

最后

關(guān)于RACSequence 和 RACTuple底層實現(xiàn)分析都已經(jīng)分析完成介蛉。最后請大家多多指教萌庆。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市币旧,隨后出現(xiàn)的幾起案子践险,更是在濱河造成了極大的恐慌,老刑警劉巖吹菱,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件巍虫,死亡現(xiàn)場離奇詭異,居然都是意外死亡毁葱,警方通過查閱死者的電腦和手機垫言,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來倾剿,“玉大人筷频,你說我怎么就攤上這事∏岸唬” “怎么了凛捏?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長芹缔。 經(jīng)常有香客問我坯癣,道長,這世上最難降的妖魔是什么最欠? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任示罗,我火速辦了婚禮,結(jié)果婚禮上芝硬,老公的妹妹穿的比我還像新娘蚜点。我一直安慰自己,他們只是感情好拌阴,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布绍绘。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陪拘。 梳的紋絲不亂的頭發(fā)上厂镇,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音左刽,去河邊找鬼捺信。 笑死,一個胖子當(dāng)著我的面吹牛悠反,可吹牛的內(nèi)容都是我干的残黑。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼斋否,長吁一口氣:“原來是場噩夢啊……” “哼梨水!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茵臭,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤疫诽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后旦委,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奇徒,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年缨硝,在試婚紗的時候發(fā)現(xiàn)自己被綠了摩钙。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡查辩,死狀恐怖胖笛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宜岛,我是刑警寧澤长踊,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站萍倡,受9級特大地震影響身弊,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜列敲,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一阱佛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧戴而,春花似錦瘫絮、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至扁眯,卻和暖如春壮莹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姻檀。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工命满, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人绣版。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓胶台,卻偏偏與公主長得像,于是被迫代替她去往敵國和親杂抽。 傳聞我的和親對象是個殘疾皇子诈唬,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容