ReactiveObjC 学习笔记 二

591 阅读5分钟

RACStream

RACStreamRACSignal 的父类。它是一个抽象类,定义了一个传输变量的“流结构”,并且声明了一些作为 Stream 结构所支持的一些方法,其中在主 @interface 下声明的方法是必须要由子类去实现的。

+ (__kindof RACStream<ValueType> *)empty;
+ (__kindof RACStream<ValueType> *)return:(nullable ValueType)value;
- (__kindof RACStream *)bind:(RACStreamBindBlock (^)(void))block;
- (__kindof RACStream *)concat:(RACStream *)stream;
- (__kindof RACStream *)zipWith:(RACStream *)stream;

+empty 方法

// RACSignal.m
+ (RACSignal *)empty {
	return [RACEmptySignal empty];
}
// RACEmptySignal.m
+ (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
}

实现很简短,直接返回了一个 RACEmptySignal ,它是一个空的 Signal ,在被订阅时立即且同步地向所有 Subscriber 发送 Completed 事件。可以猜到,它的 -subscribe: 方法干的唯一一件事应该就是“安排”(schedule),安排一个 Completed 👇

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

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

-return: 方法

// RACSignal.m
+ (RACSignal *)return:(id)value {
	return [RACReturnSignal return:value];
}
// RACReturnSignal.m
+ (RACSignal *)return:(id)value {
#ifndef DEBUG
	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 代码,实现也是很简短,直接返回一个 RACReturnSignal 的实例,value 等于传入的值。它是一个只包含一个 value 的 Signal ,在被订阅时立即且同步地向所有 Subscriber 发送包含这个 value 的 Next 事件,然后 Complete 。所以它的 -subscribe: 方法是这样的👇

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

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

-bind: 方法

- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block {
	NSCParameterAssert(block != NULL);

	/*
	 * -bind: should:
	 * 
	 * 1. Subscribe to the original signal of values.
	 * 2. Any time the original signal sends a value, transform it using the binding block.
	 * 3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
	 * 4. If the binding block asks the bind to terminate, complete the _original_ signal.
	 * 5. When _all_ signals complete, send completed to the subscriber.
	 * 
	 * If any signal sends an error at any point, send that to the subscriber.
	 */

	return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
		RACSignalBindBlock bindingBlock = block();

		__block volatile int32_t signalCount = 1;   // indicates self

		RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
		// 为突出重点,暂时省略
		void (^completeSignal)(RACDisposable *) = ^(RACDisposable *finishedDisposable) { ... };
		// 为突出重点,暂时省略
		void (^addSignal)(RACSignal *) = ^(RACSignal *signal) { ... };

		@autoreleasepool {
			RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
			[compoundDisposable addDisposable:selfDisposable];

			RACDisposable *bindingDisposable = [self subscribeNext:^(id x) {
				// Manually check disposal to handle synchronous errors.
				if (compoundDisposable.disposed) return;

				BOOL stop = NO;
				id signal = bindingBlock(x, &stop);

				@autoreleasepool {
					if (signal != nil) addSignal(signal);
					if (signal == nil || stop) {
						[selfDisposable dispose];
						completeSignal(selfDisposable);
					}
				}
			} error:^(NSError *error) {
				[compoundDisposable dispose];
				[subscriber sendError:error];
			} completed:^{
				@autoreleasepool {
					completeSignal(selfDisposable);
				}
			}];

			selfDisposable.disposable = bindingDisposable;
		}

		return compoundDisposable;
	}] setNameWithFormat:@"[%@] -bind:", self.name];
}

可见 -bind: 方法首先订阅了 self ,我们称其为 Original Signal 。每当 Original Signal 收到事件时,都会调用外部传进来的 bindingBlock ,并把事件的 value x 作为 block 的参数,产生一个新的 Signal 。随后把这个 Signal 传给 addSignal block👇

void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
			OSAtomicIncrement32Barrier(&signalCount);

			RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
			[compoundDisposable addDisposable:selfDisposable];

			RACDisposable *disposable = [signal subscribeNext:^(id x) {
				[subscriber sendNext:x];
			} error:^(NSError *error) {
				[compoundDisposable dispose];
				[subscriber sendError:error];
			} completed:^{
				@autoreleasepool {
					completeSignal(selfDisposable);
				}
			}];

			selfDisposable.disposable = disposable;
		};

在这个 block 中,它订阅了传进来的 signal ,并且将产生的所有事件透传给 -bind: 方法返回的 Signal 的 Subscriber 。

如果 bindingBlock 返回的 Signal 是 nil 或者 bindingBlockstop 的值修改为了 YES ,那就会把 selfDisposable 传给 completeBlock 👇

void (^completeSignal)(RACDisposable *) = ^(RACDisposable *finishedDisposable) {
			if (OSAtomicDecrement32Barrier(&signalCount) == 0) {
				[subscriber sendCompleted];
				[compoundDisposable dispose];
			} else {
				[compoundDisposable removeDisposable:finishedDisposable];
			}
		};

在这个 block 中,判断了当前还没有 Complete 的 Signal 数量,如果为 0 的话,将会向 -bind: 方法返回的 Signal 的 Subscriber 发送 Complete 。

其实作者在源码注释中已经对 -bind: 方法做了一个简短的介绍,在此作为总结。

-bind: should:

  1. Subscribe to the original signal of values.
  2. Any time the original signal sends a value, transform it using the binding block.
  3. If the binding block returns a signal, subscribe to it, and pass all of its values through to the subscriber as they're received.
  4. If the binding block asks the bind to terminate, complete the original signal.
  5. When all signals complete, send completed to the subscriber.

If any signal sends an error at any point, send that to the subscriber.

-concat: 方法

这个方法实现了两个 Signal 的首尾拼接。

concat | center

// Demo
RACSignal *signal0 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    NSLog(@"signal0");
    [subscriber sendNext:@"0"];
    [subscriber sendNext:@"1"];
    [subscriber sendNext:@"2"];
    [subscriber sendCompleted];
    return nil;
}];

RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    NSLog(@"signal1");
    [subscriber sendNext:@"A"];
    [subscriber sendNext:@"B"];
    [subscriber sendNext:@"C"];
    [subscriber sendNext:@"D"];
    [subscriber sendCompleted];
    return nil;
}];
[[signal0 concat:signal1] subscribeNext:^(NSString * _Nullable x) {
	NSLog(@"concat: %@", x);
}];
// output:
// signal0
// concat: 0
// concat: 1
// concat: 2
// signal1
// concat: A
// concat: B
// concat: C
// concat: D

看一下源码实现👇

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

		RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
			[subscriber sendNext:x];
		} error:^(NSError *error) {
			[subscriber sendError:error];
		} completed:^{
			// subscribes to concatted signal when source signal completes
			RACDisposable *concattedDisposable = [signal subscribe:subscriber];
			[compoundDisposable addDisposable:concattedDisposable];
		}];

		[compoundDisposable addDisposable:sourceDisposable];
		return compoundDisposable;
	}] setNameWithFormat:@"[%@] -concat: %@", self.name, signal];
}

-concat: 方法的实现核心在于第 11 行,也就是在 Source Signal 发送 Complete 的时候,紧接着对 Concatted Signal 进行了订阅。

-zipWith: 方法

这个方法实现了两个 Signal 的绑定,用两个 Signal 发出的最新事件合并成一个 RACTuple ,并把这个 RACTuple 发给 Subscriber。当两个 Signal 中任何一个 Complete ,都会触发整个 Signal 的 Complete 。

RACTuple 是一个 RAC 库自身实现的元组结构。类似于 Swift 中的 Tuple ,它是一个可以包含不同类型元素的集合。

zip | center

// Demo
RACSignal *signal0 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    NSLog(@"signal0");
    [subscriber sendNext:@"0"];
    [subscriber sendNext:@"1"];
    [subscriber sendNext:@"2"];
    [subscriber sendCompleted];
    return nil;
}];

RACSignal *signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    NSLog(@"signal1");
    [subscriber sendNext:@"A"];
    [subscriber sendNext:@"B"];
    [subscriber sendNext:@"C"];
    [subscriber sendNext:@"D"];
    [subscriber sendCompleted];
    return nil;
}];
// output
// signal0
// signal1
// zip: (0, A)
// zip: (1, B)
// zip: (2, C)

看一下源码实现👇

- (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) = ^{ ... };
		// 为突出重点,暂时省略
		void (^sendNext)(void) = ^{ ... };

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

这个方法的实现看起来很长,但实际上逻辑很简单。它分别订阅了 selfSignal 和 传进来的 otherSignal ,每当两者中任何一个接收到 Next 事件,都会把接收到的 value 存进一个临时数组,同时调用 sendNext block 👇

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

在这个 block 中,会把两个数组的 firstObject 打包成 RACTuple ,同时它们从数组中移除,并且发送给 Subscriber 。随后调用 sendCompletedIfNecessary block 👇

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

在这个 block 中,会判断两个 Signal 是否已经 Complete 而且没有剩余的 value ,只要有一个 Signal 处于这种状态,就会对 Subscriber 发送 Complete 。