天天看點

ReactiveCocoa 學習筆記五(RACChannel)RACChannel

RACChannel

RACChannel 持有兩個信号流,這兩個信号流的訂閱者所接收到的信号量并不是來自自己所訂閱的信号流,而是另一個信号流。類似于相連的兩個終端,每個終端所産生的資料都是要傳遞到另一個終端的。

@property (nonatomic, strong, readonly) RACChannelTerminal<ValueType> *leadingTerminal;

@property (nonatomic, strong, readonly) RACChannelTerminal<ValueType> *followingTerminal;
           

RACChannelTerminal

RACChannelTerminal 是 RACSignal 的子類,并且遵循

<RACSubscriber>

協定,其持有兩個屬性,分别表示源信号流和另一個終端的源信号流。

@property (nonatomic, strong, readonly) RACSignal<ValueType> *values;

@property (nonatomic, strong, readonly) id<RACSubscriber> otherTerminal;

- (instancetype)init {
	self = [super init];

	// We don't want any starting value from the leadingSubject, but we do want
	// error and completion to be replayed.
	RACReplaySubject *leadingSubject = [[RACReplaySubject replaySubjectWithCapacity:0] setNameWithFormat:@"leadingSubject"];
	RACReplaySubject *followingSubject = [[RACReplaySubject replaySubjectWithCapacity:1] setNameWithFormat:@"followingSubject"];

	// Propagate errors and completion to everything.
	[[leadingSubject ignoreValues] subscribe:followingSubject];
	[[followingSubject ignoreValues] subscribe:leadingSubject];

	_leadingTerminal = [[[RACChannelTerminal alloc] initWithValues:leadingSubject otherTerminal:followingSubject] setNameWithFormat:@"leadingTerminal"];
	_followingTerminal = [[[RACChannelTerminal alloc] initWithValues:followingSubject otherTerminal:leadingSubject] setNameWithFormat:@"followingTerminal"];

	return self;
}
           

從 RACChannel 的初始化方法,可知,這兩個屬性都是 RACReplaySubject 類型。

#pragma mark RACSignal

- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
	return [self.values subscribe:subscriber];
}

#pragma mark <RACSubscriber>

- (void)sendNext:(id)value {
	[self.otherTerminal sendNext:value];
}
           

當訂閱某一個終端流時,都會将該終端持有的源信号流進行重播,而其重播的信号量并不是由其自身決定的,因為,從 sendNext: 方法可以知道,發送的信号量實際是被另一終端的訂閱者接收了,換言之,訂閱者接收到的重播信号量,都是另一終端發送的信号量。

RACKVOChannel

RACKVOChannel 是 RACChannel 的子類,其提供了将指定對象的指定屬性與信号流互相綁定的方法。

- (instancetype)initWithTarget:(__weak NSObject *)target keyPath:(NSString *)keyPath nilValue:(nullable ValueType)nilValue;
           

該方法中代碼片段,如下:

RACDisposable *observationDisposable = [strongTarget rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
	if (!causedByDealloc && affectedOnlyLastComponent && self.currentThreadData.ignoreNextUpdate) {
		[self destroyCurrentThreadData];
		return;
	}

	[self.leadingTerminal sendNext:value];
}];
           

可見,當該指定屬性發生變動時,便由 self.leadingTerminal 信号流傳遞一個信号量,當然,這個信号量會被 self.followingTerminal 信号流接收到。

[[self.leadingTerminal
	finally:^{
		[observationDisposable dispose];
	}]
	subscribeNext:^(id x) {

		NSObject *object = (keyPathComponentsCount > 1 ? [self.target valueForKeyPath:keyPathByDeletingLastKeyPathComponent] : self.target);
		if (object == nil) return;

		[self createCurrentThreadData];
		self.currentThreadData.ignoreNextUpdate = YES;

		[object setValue:x ?: nilValue forKey:lastKeyPathComponent];
	} error:^(NSError *error) {
		NSCAssert(NO, @"Received error in %@: %@", self, error);
		NSLog(@"Received error in %@: %@", self, error);
}];
           

當 self.leadingTerminal 信号流接收到信号量時,便會去修改相應的屬性值,而 self.leadingTerminal 信号流接收到的信号量則是由 self.followingTerminal 信号流傳遞的。

但是,通常不必使用該子類,而是直接使用架構中提供的宏。

#define RACChannelTo(TARGET, ...) \
    metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
        (RACChannelTo_(TARGET, __VA_ARGS__, nil)) \
        (RACChannelTo_(TARGET, __VA_ARGS__))

#define RACChannelTo_(TARGET, KEYPATH, NILVALUE) \
    [[RACKVOChannel alloc] initWithTarget:(TARGET) keyPath:@keypath(TARGET, KEYPATH) nilValue:(NILVALUE)][@keypath(RACKVOChannel.new, followingTerminal)]
           

RACChannelTo() 在等号左側或右側代表不同的含義,使用方法如下:

RACChannelTerminal *integerChannel = RACChannelTo(self, integerProperty, @42);
[integerChannel sendNext:@5];

[integerChannel subscribeNext:^(id value) {
	NSLog(@"value: %@", value);
}];
           

RACChannelTo() 在等号右側,那麼,傳回一個信号流,實際就是 RACKVOChannel 執行個體對象的 followingTerminal 屬性。訂閱該信号流便可以監聽屬性值的變化,而使用該信号流傳遞信号量,便會改變屬性的值。

RACChannelTo(view, objectProperty) = RACChannelTo(model, objectProperty);
RACChannelTo(view, integerProperty, @2) = RACChannelTo(model, integerProperty, @10);
           

RACChannelTo() 在等号左側,那麼右側需要有相應的 RACChannelTerminal 執行個體對象與之對應,表示兩者的互相綁定。

上面兩行代碼,就是控件屬性同模型變量互相綁定的執行個體。