RACChannel

RACChannel是一对一的双向的数据流

RACChannel

在channel中向外暴露的有两个属性terminal

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

初始化terminal方法

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

    RACReplaySubject *leadingSubject = [RACReplaySubject replaySubjectWithCapacity:0];
    RACReplaySubject *followingSubject = [RACReplaySubject replaySubjectWithCapacity:1];
    
    [[leadingSubject ignoreValues] subscribe:followingSubject];
    [[followingSubject ignoreValues] subscribe:leadingSubject];

    _leadingTerminal = [[RACChannelTerminal alloc] initWithValues:leadingSubject otherTerminal:followingSubject];
    _followingTerminal = [[RACChannelTerminal alloc] initWithValues:followingSubject otherTerminal:leadingSubject]
    return self;
}
  1. 可以看到leadingSubject是由Capacity:0RACReplaySubjectfollowingSubject是由Capacity:1RACReplaySubject
  2. ignoreValues通过订阅相互信号的错误信息,当一端出现错误信息能保证相互发送,防止当一方出错时 另外一方还在工作
  3. followingSubjectCapacity为1,有初始值,因此当订阅followingTerminal信息时 可以直接进行初始化状态

RACChannelTerminal: RACSignal< RACSubscriber>

初始化

ChannelTerminal初始化,使用一个用于subcribe的RACSignal的value 和一个用于发送值的id<RACSubscriber>的otherTerminal

期指那个value表示当前端点,otherTerminal表示远程端点

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

- (instancetype)initWithValues:(RACSignal *)values otherTerminal:(id<RACSubscriber>)otherTerminal {
    self = [super init];
    _values = values;
    _otherTerminal = otherTerminal;
    return self;
}

订阅信号

订阅信号 其实订阅和接收的是self.values的信号,即订阅的时当前信号消息

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

发送

如果向Terminal发送消息 会被转发到远程端点上,因此当前端点的订阅者并不能接收到当前端点发送的消息

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

使用

我们不应该直接初始化RACChannelTerminal实例,而可以通过 初始化RACChannle的方式生成

RACChannel的使用

RACChannel+UIControl

UIControl rac_channelForControlEvents:(UIControlEvents)controlEvents key:(NSString *)key nilValue:(nullable id)nilValue {
    RACChannel *channel = [[RACChannel alloc] init];
    
    RACSignal *eventSignal = [[[self
        rac_signalForControlEvents:controlEvents]
        mapReplace:key]
        takeUntil:[[channel.followingTerminal
            ignoreValues]
            catchTo:RACSignal.empty]];
            
            [[self
        rac_liftSelector:@selector(valueForKey:) withSignals:eventSignal, nil]
        subscribe:channel.followingTerminal];
        
        RACSignal *valuesSignal = [channel.followingTerminal
        map:^(id value) {
            return value ?: nilValue;
        }];
    [self rac_liftSelector:@selector(setValue:forKey:) withSignals:valuesSignal, [RACSignal return:key], nil];

    return channel.leadingTerminal;
}

  1. eventSignal 获取事件触发的signal 并将error转换为empty信号,发送的值即为key
  2. rac_liftSelector:with: 执行一个selector方法,rac_liftSelector:@selector(valueForKey:)即为获取UIControl的事件的key的值
  3. subscribe:channel.followingTerminal 即为 channel的followingTerminal订阅, 综合起来就是 每次事件触发时 获取key的value值 然后发送followingTerminal 而followTerminal的sendNext,触发leadingSubject的sendNext,即将事件触发时获取到的value值发送给leadingSubject的订阅者 即 leadingTerminal的订阅者
  4. valueSignal 订阅followingTerminal即 订阅 followSubject的消息,即为收到leadingTerminal发送的消息

  5. [self rac_liftSelector:@selector(setValue:forKey:) withSignals:valuesSignal, [RACSignal return:key], nil]; 收到leadingTerminal发送的消息后将收到的值传给当前的控件,保证数据同步

综合起来来说,就是在controlEvent事件发生时,将key的value值发送给followingTerminal,并在接收来自leadingTerminal消息后,更新控件的key属性对应的value值

UITextField+TextChannel

- (RACChannelTerminal *)rac_newTextChannel {
    return [self rac_channelForControlEvents:UIControlEventAllEditingEvents key:@keypath(self.text) nilValue:@""];
}

RACChannel+KVO

RACChannel提供了RACKVOChannel来实现对象的某个属性值进行观测, 常可以用来进行与UIKit组件或者其他对象属性进行高效的双向绑定

RACKVOChannel :RACChannel

在KVO的channel中 我们常通过RACChannelTo宏定义来直接使用,观测某对象属性

//参数分别为 对象、属性和默认值
#define RACChannelTo_(TARGET, KEYPATH, NILVALUE)

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

例如:

RACChannelTerminal *integerChannel = [[RACKVOChannel alloc] initWithTarget:self keyPath:@"integerProperty" nilValue:@42][@"followingTerminal"];

先来看RACKVOChannel的初始化方法

- (instancetype)initWithTarget:(__weak NSObject *)target keyPath:(NSString *)keyPath nilValue:(id)nilValue {

    //KVO对象属性值并发送
    [strongTarget rac_observeKeyPath:keyPath options:NSKeyValueObservingOptionInitial observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
        [self.leadingTerminal sendNext:value];
    }]
    
    // 收到订阅值 并设置对象的属性值
    [[self.leadingTerminal
        finally:^{
            [observationDisposable dispose];
        }]
        subscribeNext:^(id x) {
            NSObject *object = (keyPathComponentsCount > 1 ? [self.target valueForKeyPath:keyPathByDeletingLastKeyPathComponent] : self.target);
            if (object == nil) return;
            [object setValue:x ?: nilValue forKey:lastKeyPathComponent];
        } error:^(NSError *error) {
        }];
    
}

使用RACKVOChannel的初始化方法即获得了一个Channel对象,对此对象执行 [@keypath(RACKVOChannel.new, followingTerminal)]来获取terminal对象是因为重写了以下两个方法:

- (RACChannelTerminal *)objectForKeyedSubscript:(NSString *)key {
    RACChannelTerminal *terminal = [self valueForKey:key];
    return terminal;
}

- (void)setObject:(RACChannelTerminal *)otherTerminal forKeyedSubscript:(NSString *)key {
    RACChannelTerminal *selfTerminal = [self objectForKeyedSubscript:key];
    [otherTerminal subscribe:selfTerminal];
    [[selfTerminal skip:1] subscribe:otherTerminal];
}

重写这两个方法 就会使在调用setObject: forKeyedSubscript:时执行-subscribe:方法完成双向绑定