RACSequence分析(二)—— RACDynamicSequence

本文详细解析了RACDynamicSequence类的功能与实现原理,包括其继承自RACSequence的特性,以及如何通过lazy initialization实现高效的数据序列处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RACDynamicSequence 作为RACSequence的子类,只提供了一个新的方法sequenceWithLazyDependency:headBlock:tailBlock:,供父类bind:passingThroughValuesFromSequence:方法中调用。

完整测试用例在这里

打开.m文件,从上往下看代码:
* #define DEALLOC_OVERFLOW_GUARD 100 定义一个数值决定最大同时释放RACDynamicSequence对象的个数,来避免栈溢出。
* _head 对应于父类属性head
* _tail 对应于父类属性tail
* _dependency 存储dependencyBlock执行的结果值。
* headBlock 存储参数headBlock
* tailBlock 存储参数tailBlock
* hasDependency 用来标识是否是通过dependencyBlock初始化的。
* dependencyBlock 存储参数dependencyBlock

接下来,看看每个方法的实现:

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

对父类方法的重写,父类的实现中,其实就是调用此类进行的初始化。
初始化RACDynamicSequence对象,并保存参数headBlock tailBlock ,并设置hasDependency值。

测试用例:

- (void)test_sequenceWithHeadBlock
{
    RACDynamicSequence *sequence = [RACDynamicSequence sequenceWithHeadBlock:^id{
        return @(1);
    } tailBlock:^RACSequence *{
        return [RACSequence return:@(2)];
    }];
    NSLog(@"sequenceWithHeadBlock -- %@", sequence);

    // 打印日志
    /*
     2018-08-14 17:16:02.801309+0800 TestRACDynamicSequence[50314:13492872] sequenceWithHeadBlock -- <RACDynamicSequence: 0x6000000973e0>{ name = , head = (unresolved), tail = (unresolved) }
     */
}

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

初始化RACDynamicSequence对象,并保存参数headBlock tailBlock dependencyBlock ,并设置hasDependency值。

测试用例:

- (void)test_sequenceWithLazyDependency
{
    RACDynamicSequence *sequence = [RACDynamicSequence sequenceWithLazyDependency:^id{
        return @(1);
    } headBlock:^id(id dependency) {
        return dependency;
    } tailBlock:^RACSequence *(id dependency) {
        return [RACSequence return:dependency];
    } ];
    NSLog(@"sequenceWithLazyDependency -- %@", sequence);

    // 打印日志
    /*
     2018-08-14 17:16:24.973383+0800 TestRACDynamicSequence[50346:13494056] sequenceWithLazyDependency -- <RACDynamicSequence: 0x604000091e90>{ name = , head = (unresolved), tail = (unresolved) }
     */
}

- (void)dealloc {
    static volatile int32_t directDeallocCount = 0;

    if (OSAtomicIncrement32(&directDeallocCount) >= DEALLOC_OVERFLOW_GUARD) {
        OSAtomicAdd32(-DEALLOC_OVERFLOW_GUARD, &directDeallocCount);

        // Put this sequence's tail onto the autorelease pool so we stop
        // recursing.
        __autoreleasing RACSequence *tail __attribute__((unused)) = _tail;
    }

    _tail = nil;
}

在销毁方法中,通过directDeallocCountDEALLOC_OVERFLOW_GUARD比较来决定是将_tail直接释放,还是放到自动释放池中,保证不会因为同时释放太多对象而导致栈溢出。


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

通过@synchronized可知,该方法中进行的是同步操作。
* 先获取headBlock,如果存在会将headBlock执行的结果赋值给_head。然后返回。
* 如果不存在,直接返回_head
由于上面两个初始化函数中存在headBlock而且父类中对该方法说明此参数必须存在,那么这里也就是返回参数headBlock执行的结果。在headBlock执行的时候会根据是否有参数dependencyBlock来获取一个值当做headBlock的参数。

测试用例:

- (void)test_head
{
    RACDynamicSequence *sequence1 = [RACDynamicSequence sequenceWithHeadBlock:^id{
        return @(1);
    } tailBlock:^RACSequence *{
        return nil;
    }];
    RACDynamicSequence *sequence2 = [RACDynamicSequence sequenceWithLazyDependency:^id{
        return @(100);
    } headBlock:^id(id dependency) {
        return dependency;
    } tailBlock:^RACSequence *(id dependency) {
        return nil;
    }];

    NSLog(@"head -- %@ -- %@", [sequence1 head], [sequence2 head]);

    // 打印日志
    /*
     2018-08-14 17:24:13.966048+0800 TestRACDynamicSequence[50694:13517182] head -- 1 -- 100
     */
}

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

该方法的作用与上面方法类似,获取参数tailBlock执行的结果。

测试用例:

- (void)test_tail
{
    RACDynamicSequence *sequence1 = [RACDynamicSequence sequenceWithHeadBlock:^id{
        return @(1);
    } tailBlock:^RACSequence *{
        return [RACSequence return:@(2)];
    }];

    RACDynamicSequence *sequence2 = [RACDynamicSequence sequenceWithLazyDependency:^id{
        return @(100);
    } headBlock:^id(id dependency) {
        return dependency;
    } tailBlock:^RACSequence *(id dependency) {
        return [RACSequence return:dependency];
    }];

    NSLog(@"tail -- %@ -- %@", [sequence1 tail], [sequence2 tail]);

    // 打印日志
    /*
     2018-08-14 17:27:35.492093+0800 TestRACDynamicSequence[50838:13527548] tail -- <RACUnarySequence: 0x60000022fda0>{ name = , head = 2 } -- <RACUnarySequence: 0x600000230a00>{ name = , head = 100 }
     */
}

- (NSString *)description {
    id head = @"(unresolved)";
    id tail = @"(unresolved)";

    @synchronized (self) {
        if (self.headBlock == nil) head = _head;
        if (self.tailBlock == nil) {
            tail = _tail;
            if (tail == self) tail = @"(self)";
        }
    }

    return [NSString stringWithFormat:@"<%@: %p>{ name = %@, head = %@, tail = %@ }", self.class, self, self.name, head, tail];
}

该方法格式化该序列对象的打印日志。


其实,上面涉及到主要功能的代码就是head tail方法,这两个方法第一次调用的时候才进行计算存储,方便下次使用。所以说RACDynamicSequence是个冷序列。

上一篇bind:passingThroughValuesFromSequence:返回了一个RACDynamicSequence对象,那么这里就重新对bind:方法进行分析:

- (instancetype)bind:(RACStreamBindBlock)bindBlock passingThroughValuesFromSequence:(RACSequence *)passthroughSequence {
    // Store values calculated in the dependency here instead, avoiding any kind
    // of temporary collection and boxing.
    //
    // This relies on the implementation of RACDynamicSequence synchronizing
    // access to its head, tail, and dependency, and we're only doing it because
    // we really need the performance.
    __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;

            // We've exhausted the current sequence, create a sequence from the
            // next value.
            id value = valuesSeq.head;

            if (value == nil) {
                // We've exhausted all the sequences.
                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;
}

因为RACDynamicSequence不管是先调用head还是tail,都会先调用dependencyBlock,所以看下dependencyBlock的定义。

dependencyBlock中首先判断了current.head,也就是passthroughSequence.head,而passthroughSequence是方法的参数之一。由于bind:方法内部实现可以知道passthroughSequence可以为nil,所以下面就分情况分析:
* passthroughSequencenilcurrent.headnil,进行while循环,然后id value = valuesSeq.head;获取源序列selfhead,通过current = (id)bindBlock(value, &stop);获取一个新的序列,此时出现分支:
* 如果此时current不存在就会终止while循环,最终返回nil,这时headBlock获取的值也为niltailBlock也会获取到nil,所以这种情况就返回一个空序列。
* 如果此时current存在,valuesSeq就会取源序列的tail,然后进行while循环的检测,还是分支:
* current.head存在,headBlock返回current.headtailBlock对源序列的tail调用bind:passingThroughValuesFromSequence:函数进行同样的处理。
* current.head不存在,继续进行while循环。

所以这种情况下就是对源序列的`value`通过`bindBlock`做处理,得到新的值,如果新的值也是序列,就获取新的序列的值。

* passthroughSequence不为nil。同样先进行while循环,此时current.head出现分支:
* 如果为nil,跟上面步骤一样。
* 如果不为nil,直接返回nil。然后headBlock获取到passthroughSequencehead,tailBlock通过源序列递归调用bind:passingThroughValuesFromSequence:继续进行。

所以这种情况先是获取`passthroughSequence`的值,然后对源序列处理,通过`bindBlock`对源序列的值做处理得到新的值。

测试用例:

- (void)test_bind_pass
{
    RACSequence *sequence = [RACSequence return:@(1)];
    RACStreamBindBlock (^bindBlock)(void) = ^RACStreamBindBlock{
        return ^(id value, BOOL *stop) {
            return [RACSequence return:@(100 + [value intValue])];
        };
    };
    RACSequence *sequence1 = [sequence bind:bindBlock];

    RACSequence *passSequence = [RACSequence return:@(2)];
    RACSequence *sequence2 = [sequence bind:bindBlock() passingThroughValuesFromSequence:passSequence];

    NSLog(@"bind_pass -- %@ -- %@ -- %@ -- %@", sequence1.head, sequence1.tail, sequence1.tail.head, sequence1.tail.tail);
    NSLog(@"bind_pass -- %@ -- %@ -- %@ -- %@", sequence2.head, sequence2.tail, sequence2.tail.head, sequence2.tail.tail);

    // 打印日志
    /*
     2018-08-14 19:04:35.033489+0800 TestRACDynamicSequence[53175:13675544] bind_pass -- 101 -- (null) -- (null) -- (null)
     2018-08-14 19:04:35.033765+0800 TestRACDynamicSequence[53175:13675544] bind_pass -- 2 -- <RACDynamicSequence: 0x600000286900>{ name = , head = 101, tail = (null) } -- 101 -- (null)
     */
}
上面就是对RACDynamicSequence的分析以及对bind:方法的重分析,后面会继续分析RACSequence的子类。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值