123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- //
- // RACSequence.m
- // ReactiveObjC
- //
- // Created by Justin Spahr-Summers on 2012-10-29.
- // Copyright (c) 2012 GitHub. All rights reserved.
- //
- #import "RACSequence.h"
- #import "RACArraySequence.h"
- #import "RACDynamicSequence.h"
- #import "RACEagerSequence.h"
- #import "RACEmptySequence.h"
- #import "RACScheduler.h"
- #import "RACSignal.h"
- #import "RACSubscriber.h"
- #import "RACTuple.h"
- #import "RACUnarySequence.h"
- // An enumerator over sequences.
- @interface RACSequenceEnumerator : NSEnumerator
- // The sequence the enumerator is enumerating.
- //
- // This will change as the enumerator is exhausted. This property should only be
- // accessed while synchronized on self.
- @property (nonatomic, strong) RACSequence *sequence;
- @end
- @interface RACSequence ()
- // Performs one iteration of lazy binding, passing through values from `current`
- // until the sequence is exhausted, then recursively binding the remaining
- // values in the receiver.
- //
- // Returns a new sequence which contains `current`, followed by the combined
- // result of all applications of `block` to the remaining values in the receiver.
- - (RACSequence *)bind:(RACSequenceBindBlock)block passingThroughValuesFromSequence:(RACSequence *)current;
- @end
- @implementation RACSequenceEnumerator
- - (id)nextObject {
- id object = nil;
-
- @synchronized (self) {
- object = self.sequence.head;
- self.sequence = self.sequence.tail;
- }
-
- return object;
- }
- @end
- @implementation RACSequence
- #pragma mark Lifecycle
- + (RACSequence *)sequenceWithHeadBlock:(id (^)(void))headBlock tailBlock:(RACSequence<id> *(^)(void))tailBlock {
- return [[RACDynamicSequence sequenceWithHeadBlock:headBlock tailBlock:tailBlock] setNameWithFormat:@"+sequenceWithHeadBlock:tailBlock:"];
- }
- #pragma mark Class cluster primitives
- - (id)head {
- NSCAssert(NO, @"%s must be overridden by subclasses", __func__);
- return nil;
- }
- - (RACSequence *)tail {
- NSCAssert(NO, @"%s must be overridden by subclasses", __func__);
- return nil;
- }
- #pragma mark RACStream
- + (RACSequence *)empty {
- return RACEmptySequence.empty;
- }
- + (RACSequence *)return:(id)value {
- return [RACUnarySequence return:value];
- }
- - (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 {
- // 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;
- }
- - (RACSequence *)concat:(RACSequence *)sequence {
- NSCParameterAssert(sequence != nil);
- return [[[RACArraySequence sequenceWithArray:@[ self, sequence ] offset:0]
- flatten]
- setNameWithFormat:@"[%@] -concat: %@", self.name, sequence];
- }
- - (RACSequence *)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];
- }
- #pragma mark Extended methods
- - (NSArray *)array {
- NSMutableArray *array = [NSMutableArray array];
- for (id obj in self) {
- [array addObject:obj];
- }
- return [array copy];
- }
- - (NSEnumerator *)objectEnumerator {
- RACSequenceEnumerator *enumerator = [[RACSequenceEnumerator alloc] init];
- enumerator.sequence = self;
- return enumerator;
- }
- - (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];
- }
- - (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);
- }
- - (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;
- }
- - (id)objectPassingTest:(BOOL (^)(id))block {
- NSCParameterAssert(block != NULL);
- return [self filter:block].head;
- }
- - (RACSequence *)eagerSequence {
- return [RACEagerSequence sequenceWithArray:self.array offset:0];
- }
- - (RACSequence *)lazySequence {
- return self;
- }
- #pragma mark NSCopying
- - (id)copyWithZone:(NSZone *)zone {
- return self;
- }
- #pragma mark NSCoding
- - (Class)classForCoder {
- // Most sequences should be archived as RACArraySequences.
- return RACArraySequence.class;
- }
- - (id)initWithCoder:(NSCoder *)coder {
- if (![self isKindOfClass:RACArraySequence.class]) return [[RACArraySequence alloc] initWithCoder:coder];
- // Decoding is handled in RACArraySequence.
- return [super init];
- }
- - (void)encodeWithCoder:(NSCoder *)coder {
- [coder encodeObject:self.array forKey:@"array"];
- }
- #pragma mark NSFastEnumeration
- - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(__unsafe_unretained id *)stackbuf count:(NSUInteger)len {
- if (state->state == ULONG_MAX) {
- // Enumeration has completed.
- return 0;
- }
- // We need to traverse the sequence itself on repeated calls to this
- // method, so use the 'state' field to track the current head.
- RACSequence *(^getSequence)(void) = ^{
- return (__bridge RACSequence *)(void *)state->state;
- };
- void (^setSequence)(RACSequence *) = ^(RACSequence *sequence) {
- // Release the old sequence and retain the new one.
- CFBridgingRelease((void *)state->state);
- state->state = (unsigned long)CFBridgingRetain(sequence);
- };
- void (^complete)(void) = ^{
- // Release any stored sequence.
- setSequence(nil);
- state->state = ULONG_MAX;
- };
- if (state->state == 0) {
- // Since a sequence doesn't mutate, this just needs to be set to
- // something non-NULL.
- state->mutationsPtr = state->extra;
- setSequence(self);
- }
- state->itemsPtr = stackbuf;
- NSUInteger enumeratedCount = 0;
- while (enumeratedCount < len) {
- RACSequence *seq = getSequence();
- // Because the objects in a sequence may be generated lazily, we want to
- // prevent them from being released until the enumerator's used them.
- __autoreleasing id obj = seq.head;
- if (obj == nil) {
- complete();
- break;
- }
- stackbuf[enumeratedCount++] = obj;
- if (seq.tail == nil) {
- complete();
- break;
- }
- setSequence(seq.tail);
- }
- return enumeratedCount;
- }
- #pragma mark NSObject
- - (NSUInteger)hash {
- return [self.head hash];
- }
- - (BOOL)isEqual:(RACSequence *)seq {
- if (self == seq) return YES;
- if (![seq isKindOfClass:RACSequence.class]) return NO;
- for (id<NSObject> selfObj in self) {
- id<NSObject> seqObj = seq.head;
- // Handles the nil case too.
- if (![seqObj isEqual:selfObj]) return NO;
- seq = seq.tail;
- }
- // self is now depleted -- the argument should be too.
- return (seq.head == nil);
- }
- @end
|