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 实例对象与之对应,表示两者的相互绑定。
上面两行代码,就是控件属性同模型变量相互绑定的实例。