RACSignal的使用方法 - 对RACStream的实现(二)

上篇文章 讲到RACStream中主要的方法就是bind:flattenMap:,而flattenMap:也是通过调用bind:方法实现的,那bind:方法的实质是什么?其实根据bind:方法注释的几个点,可以知道其就是对信号的信号进行的订阅,获取到信号的信号所发送的值信息。正如flattenMap:方法的参数可以获取一个RACStream对象,flatten的注释Returns a stream consisting of the combined streams obtained from the receiver.
所以,RACStrem中其他函数也会首先将值转换为信号,然后在bind:中使用。下面看一看RACStrem (Operations)中的方法:

- (instancetype)flattenMap:(RACStream * (^)(id value))block {
 Class class = self.class;

 return [[self bind:^{
  return ^(id value, BOOL *stop) {
id stream = block(value) ?: [class empty];
NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);

   return stream;
  };
 }] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}

id stream = block(value) ?: [class empty];就是将一个value转换成了信号。


- (instancetype)flatten {
   __weak RACStream *stream __attribute__((unused)) = self;
   return [[self flattenMap:^(id value) {
return value;
   }] setNameWithFormat:@"[%@] -flatten", self.name];
  }

返回value,根据NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);可知value就是一个信号类型的对象。


- (instancetype)map:(id (^)(id value))block {
 NSCParameterAssert(block != nil);

 Class class = self.class;

 return [[self flattenMap:^(id value) {
  return [class return:block(value)];
 }] setNameWithFormat:@"[%@] -map:", self.name];
}

通过调用类方法return:value转换成信号。对于return:下面会具体讲解。


- (instancetype)mapReplace:(id)object {
  return [[self map:^(id _) {
   return object;
  }] setNameWithFormat:@"[%@] -mapReplace: %@", self.name, [object rac_description]];
 }

通过map:方法将object转换成了信号。


- (instancetype)combinePreviousWithStart:(id)start reduce:(id (^)(id previous, id next))reduceBlock {
 NSCParameterAssert(reduceBlock != NULL);
    return [[[self
        scanWithStart:RACTuplePack(start)
        reduce:^(RACTuple *previousTuple, id next) {
            id value = reduceBlock(previousTuple[0], next);
            return RACTuplePack(next, value);
        }]
        map:^(RACTuple *tuple) {
            return tuple[1];
        }]
        setNameWithFormat:@"[%@] -combinePreviousWithStart: %@ reduce:", self.name, [start rac_description]];
}

后面通过一个map:方法,将tuple[1]转换成了一个信号。而前面完成了什么功能呢?继续看代码

- (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))reduceBlock {
    NSCParameterAssert(reduceBlock != nil);

    return [[self
        scanWithStart:startingValue
        reduceWithIndex:^(id running, id next, NSUInteger index) {
            return reduceBlock(running, next);
        }]
        setNameWithFormat:@"[%@] -scanWithStart: %@ reduce:", self.name, [startingValue rac_description]];
}
- (instancetype)scanWithStart:(id)startingValue reduceWithIndex:(id (^)(id, id, NSUInteger))reduceBlock {
    NSCParameterAssert(reduceBlock != nil);

    Class class = self.class;

    return [[self bind:^{
        __block id running = startingValue;
        __block NSUInteger index = 0;

        return ^(id value, BOOL *stop) {
            running = reduceBlock(running, value, index++);
            return [class return:running];
        };
    }] setNameWithFormat:@"[%@] -scanWithStart: %@ reduceWithIndex:", self.name, [startingValue rac_description]];
}

- (instancetype)scanWithStart:(id)startingValue reduceWithIndex:(id (^)(id, id, NSUInteger))reduceBlock方法中,通过running = reduceBlock(running, value, index++);完成对running的计算和index的增加。然后通过return:转换成一个信号。这个方法的功能就是通过reduceBlock完成信号发送新值与之前值的运算,然后把结果作为新值发送。

- (instancetype)scanWithStart:(id)startingValue reduce:(id (^)(id running, id next))reduceBlock只是对- (instancetype)scanWithStart:(id)startingValue reduceWithIndex:(id (^)(id, id, NSUInteger))reduceBlock的简单调用,不使用index罢了。


- (instancetype)filter:(BOOL (^)(id value))block {
    NSCParameterAssert(block != nil);

    Class class = self.class;

    return [[self flattenMap:^ id (id value) {
        if (block(value)) {
            return [class return:value];
        } else {
            return class.empty;
        }
    }] setNameWithFormat:@"[%@] -filter:", self.name];
}

通过block获得筛选条件,并将value转换成一个信号。


- (instancetype)ignore:(id)value {
    return [[self filter:^ BOOL (id innerValue) {
        return innerValue != value && ![innerValue isEqual:value];
    }] setNameWithFormat:@"[%@] -ignore: %@", self.name, [value rac_description]];
}

相当于filter:反操作。


- (instancetype)reduceEach:(id (^)())reduceBlock {
    NSCParameterAssert(reduceBlock != nil);

    __weak RACStream *stream __attribute__((unused)) = self;
    return [[self map:^(RACTuple *t) {
        NSCAssert([t isKindOfClass:RACTuple.class], @"Value from stream %@ is not a tuple: %@", stream, t);
        return [RACBlockTrampoline invokeBlock:reduceBlock withArguments:t];
    }] setNameWithFormat:@"[%@] -reduceEach:", self.name];
}

通过map:[RACBlockTrampoline invokeBlock:reduceBlock withArguments:t]的值转换成信号,而RACBlockTrampoline就是一个单独调用block获取值的类,支持的参数范围是0-15。


- (instancetype)startWith:(id)value {
 return [[[self.class return:value]
        concat:self]
        setNameWithFormat:@"[%@] -startWith: %@", self.name, [value rac_description]];
}

首先通过return:value转换为信号,然后调用concat:方法。
RACStream没有实现concat:,看下RACSignal里面的实现:

- (RACSignal *)concat:(RACSignal *)signal {
    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        RACSerialDisposable *serialDisposable = [[RACSerialDisposable alloc] init];

        RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
            [subscriber sendNext:x];
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            RACDisposable *concattedDisposable = [signal subscribe:subscriber];
            serialDisposable.disposable = concattedDisposable;
        }];

        serialDisposable.disposable = sourceDisposable;
        return serialDisposable;
    }] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
 }

其实就是信号的拼接,先对当前的信号做订阅,如果订阅完成,接着订阅下一个(拼接)的信号。


- (instancetype)skip:(NSUInteger)skipCount {
    Class class = self.class;

    return [[self bind:^{
        __block NSUInteger skipped = 0;

        return ^(id value, BOOL *stop) {
            if (skipped >= skipCount) return [class return:value];

            skipped++;
            return class.empty;
        };
    }] setNameWithFormat:@"[%@] -skip: %lu", self.name, (unsigned long)skipCount];
}

根据skipCount决定跳过几个值,最终还是通过return: empty返回一个信号。


- (instancetype)take:(NSUInteger)count {
    Class class = self.class;

    if (count == 0) return class.empty;

    return [[self bind:^{
        __block NSUInteger taken = 0;

        return ^ id (id value, BOOL *stop) {
            if (taken < count) {
                ++taken;
                if (taken == count) *stop = YES;
                return [class return:value];
            } else {
                return nil;
            }
        };
    }] setNameWithFormat:@"[%@] -take: %lu", self.name, (unsigned long)count];
}

作用与skip:相反,返回值还是通过return:转换为了信号。


+ (instancetype)join:(id<NSFastEnumeration>)streams block:(RACStream * (^)(id, id))block {
    RACStream *current = nil;

    // Creates streams of successively larger tuples by combining the input
    // streams one-by-one.
    for (RACStream *stream in streams) {
        // For the first stream, just wrap its values in a RACTuple. That way,
        // if only one stream is given, the result is still a stream of tuples.
        if (current == nil) {
            current = [stream map:^(id x) {
                return RACTuplePack(x);
            }];

            continue;
        }

        current = block(current, stream);
    }

    if (current == nil) return [self empty];

    return [current map:^(RACTuple *xs) {
        // Right now, each value is contained in its own tuple, sorta like:
        //
        // (((1), 2), 3)
        //
        // We need to unwrap all the layers and create a tuple out of the result.
        NSMutableArray *values = [[NSMutableArray alloc] init];

        while (xs != nil) {
            [values insertObject:xs.last ?: RACTupleNil.tupleNil atIndex:0];
            xs = (xs.count > 1 ? xs.first : nil);
        }

        return [RACTuple tupleWithObjectsFromArray:values];
    }];
}

将多个streams通过block转换成一个RACStream *current,然后返回多个streams的值组成的一个RACTuple值。


+ (instancetype)zip:(id<NSFastEnumeration>)streams {
    return [[self join:streams block:^(RACStream *left, RACStream *right) {
        return [left zipWith:right];
    }] setNameWithFormat:@"+zip: %@", streams];
}

调用上一个方法,只是block的实现由zipWith:方法决定。同样看下RACSignal下的实现:

- (RACSignal *)zipWith:(RACSignal *)signal {
    NSCParameterAssert(signal != nil);

    return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        __block BOOL selfCompleted = NO;
        NSMutableArray *selfValues = [NSMutableArray array];

        __block BOOL otherCompleted = NO;
        NSMutableArray *otherValues = [NSMutableArray array];

        void (^sendCompletedIfNecessary)(void) = ^{
            @synchronized (selfValues) {
                BOOL selfEmpty = (selfCompleted && selfValues.count == 0);
                BOOL otherEmpty = (otherCompleted && otherValues.count == 0);
                if (selfEmpty || otherEmpty) [subscriber sendCompleted];
            }
        };

        void (^sendNext)(void) = ^{
            @synchronized (selfValues) {
                if (selfValues.count == 0) return;
                if (otherValues.count == 0) return;

                RACTuple *tuple = RACTuplePack(selfValues[0], otherValues[0]);
                [selfValues removeObjectAtIndex:0];
                [otherValues removeObjectAtIndex:0];

                [subscriber sendNext:tuple];
                sendCompletedIfNecessary();
            }
        };

        RACDisposable *selfDisposable = [self subscribeNext:^(id x) {
            @synchronized (selfValues) {
                [selfValues addObject:x ?: RACTupleNil.tupleNil];
                sendNext();
            }
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            @synchronized (selfValues) {
                selfCompleted = YES;
                sendCompletedIfNecessary();
            }
        }];

        RACDisposable *otherDisposable = [signal subscribeNext:^(id x) {
            @synchronized (selfValues) {
                [otherValues addObject:x ?: RACTupleNil.tupleNil];
                sendNext();
            }
        } error:^(NSError *error) {
            [subscriber sendError:error];
        } completed:^{
            @synchronized (selfValues) {
                otherCompleted = YES;
                sendCompletedIfNecessary();
            }
        }];

        return [RACDisposable disposableWithBlock:^{
            [selfDisposable dispose];
            [otherDisposable dispose];
        }];
    }] setNameWithFormat:@"[%@] -zipWith: %@", self.name, signal];
}

通过sendNext的定义可以看出,该方法是将两个信号的最新值组合成一个元祖值返回,任意一个完成就结束整个信号。


+ (instancetype)zip:(id<NSFastEnumeration>)streams reduce:(id (^)())reduceBlock {
    NSCParameterAssert(reduceBlock != nil);

    RACStream *result = [self zip:streams];

    // Although we assert this condition above, older versions of this method
    // supported this argument being nil. Avoid crashing Release builds of
    // apps that depended on that.
    if (reduceBlock != nil) result = [result reduceEach:reduceBlock];

    return [result setNameWithFormat:@"+zip: %@ reduce:", streams];
}

通过zip:将多个信号的值转换成元祖值,再通过reduceBlock实现对值的处理。


+ (instancetype)concat:(id<NSFastEnumeration>)streams {
    RACStream *result = self.empty;
    for (RACStream *stream in streams) {
        result = [result concat:stream];
    }

    return [result setNameWithFormat:@"+concat: %@", streams];
}

将多个信号通过实例方法concat:连接起来。


- (instancetype)takeUntilBlock:(BOOL (^)(id x))predicate {
    NSCParameterAssert(predicate != nil);

    Class class = self.class;

    return [[self bind:^{
        return ^ id (id value, BOOL *stop) {
            if (predicate(value)) return nil;

            return [class return:value];
        };
    }] setNameWithFormat:@"[%@] -takeUntilBlock:", self.name];
}

通过predicate获取一个终止的条件。


- (instancetype)takeWhileBlock:(BOOL (^)(id x))predicate {
    NSCParameterAssert(predicate != nil);

    return [[self takeUntilBlock:^ BOOL (id x) {
        return !predicate(x);
    }] setNameWithFormat:@"[%@] -takeWhileBlock:", self.name];
}

takeUntilBlock:获取一个开始的条件。


- (instancetype)skipUntilBlock:(BOOL (^)(id x))predicate {
    NSCParameterAssert(predicate != nil);

    Class class = self.class;

    return [[self bind:^{
        __block BOOL skipping = YES;

        return ^ id (id value, BOOL *stop) {
            if (skipping) {
                if (predicate(value)) {
                    skipping = NO;
                } else {
                    return class.empty;
                }
            }

            return [class return:value];
        };
    }] setNameWithFormat:@"[%@] -skipUntilBlock:", self.name];
}

通过predicate获取发送的条件。


- (instancetype)skipWhileBlock:(BOOL (^)(id x))predicate {
    NSCParameterAssert(predicate != nil);

    return [[self skipUntilBlock:^ BOOL (id x) {
        return !predicate(x);
    }] setNameWithFormat:@"[%@] -skipWhileBlock:", self.name];
}

skipUntilBlock:相反。


- (instancetype)distinctUntilChanged {
    Class class = self.class;

    return [[self bind:^{
        __block id lastValue = nil;
        __block BOOL initial = YES;

        return ^(id x, BOOL *stop) {
            if (!initial && (lastValue == x || [x isEqual:lastValue])) return [class empty];

            initial = NO;
            lastValue = x;
            return [class return:x];
        };
    }] setNameWithFormat:@"[%@] -distinctUntilChanged", self.name];
 }

通过保存当前值,来判断下一个值会不会发送,如果一致不发送,如果不一致就发送。也就是说每次发送的值都是不一样的。


现在已经把RACStream中的所有方法分析了一遍,里面涉及到的return:方法还没有讲到,现在看下return:方法:
+ (RACSignal *)return:(id)value {
    return [RACReturnSignal return:value];
}
+ (RACSignal *)return:(id)value {
#ifndef DEBUG
    // In release builds, use singletons for two very common cases.
    if (value == RACUnit.defaultUnit) {
        static RACReturnSignal *unitSingleton;
        static dispatch_once_t unitPred;

        dispatch_once(&unitPred, ^{
            unitSingleton = [[self alloc] init];
            unitSingleton->_value = RACUnit.defaultUnit;
        });

        return unitSingleton;
    } else if (value == nil) {
        static RACReturnSignal *nilSingleton;
        static dispatch_once_t nilPred;

        dispatch_once(&nilPred, ^{
            nilSingleton = [[self alloc] init];
            nilSingleton->_value = nil;
        });

        return nilSingleton;
    }
#endif

    RACReturnSignal *signal = [[self alloc] init];
    signal->_value = value;

#ifdef DEBUG
    [signal setNameWithFormat:@"+return: %@", value];
#endif

    return signal;
}

在debug模式下,会判断value的值来创建单例RACUnit RACReturnSignal,其他情况都是创建一个新的对象,然后将value值保存下来以备用。什么时候用呢?当然是订阅的时候,看下订阅函数:

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    return [RACScheduler.subscriptionScheduler schedule:^{
        [subscriber sendNext:self.value];
        [subscriber sendCompleted];
    }];
}

可以看到,当该信号被订阅时,会发送其保留的值,然后发送一个完成信息。这就是上面那些函数中用于将值转换为信号的类。其实还遗漏了一个empty方法,同样看下RACSignal中的实现:

+ (RACSignal *)empty {
    return [RACEmptySignal empty];
}
+ (RACSignal *)empty {
#ifdef DEBUG
    // Create multiple instances of this class in DEBUG so users can set custom
    // names on each.
    return [[[self alloc] init] setNameWithFormat:@"+empty"];
#else
    static id singleton;
    static dispatch_once_t pred;

    dispatch_once(&pred, ^{
        singleton = [[self alloc] init];
    });

    return singleton;
#endif
}

在debug下创建新的对象,是为了设置name以便调试bug。release下是一个单例类。继续看订阅函数:

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
    NSCParameterAssert(subscriber != nil);

    return [RACScheduler.subscriptionScheduler schedule:^{
        [subscriber sendCompleted];
    }];
}

订阅的时候发送完成信息,结束信号的订阅。

上面把RACStream中的方法分析完了,下一篇会分析RACSignal中的方法。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值