RAC bind
2018-04-24 本文已影响36人
哦呵呵y
系列文章
RACSignal,RACSubject,RACReplaySubject
RAC bind
RAC Merge flatten
RACMulticastConnection
RAC switchToLatest
RAC 的核心操作思想,HOOK,而核心方法bind 就是hook使用的体现。
- 在oc中使用hook,可以在方法调用中,替换原方法的实现。rac中也是一样,可以利用hook的思想,在信号和订阅者之间,加入一层新的逻辑。
- 正常来说我们发送信号只有信号的订阅者才会接收到消息,而bind会将信号拦截过滤后发送到新的信号订阅者中。
bind的使用:
RACSubject *subject = [RACSubject subject];
RACSignal *signal = [subject bind:^RACSignalBindBlock _Nonnull{
NSLog(@"有订阅者订阅"); // 第二步 订阅信后会触发这里
// MAKR_1 此block 用来过滤信号 在原信号订阅者中会调用
return ^(id value, BOOL *stop) {
NSLog(@"收到原信号数据"); // 第四步 收到信号后,处理完,返回一个新的信号
NSLog(@"过滤处理以后将新的信号返回出去")
return [RACReturnSignal return:value];
};
}];
[signal subscribeNext:^(id _Nullable x) { //第一步 订阅信号
NSLog(@"收到最终的信号");
}];
[subject sendNext:@"发送信号"]; // 第三部 发送信号
...
// bind 内部实现
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
// 当bind返回的信号被注册时会调用这里 获取到 bind传入的block用来对原数据进行包装生成新的信号
RACSignalBindBlock bindingBlock = block();
__block volatile int32_t signalCount = 1; // indicates self
RACCompoundDisposable *compoundDisposable = [RACCompoundDisposable compoundDisposable];
void (^completeSignal)(RACDisposable *) = ^(RACDisposable *finishedDisposable) {
if (OSAtomicDecrement32Barrier(&signalCount) == 0) {
[subscriber sendCompleted];
[compoundDisposable dispose];
} else {
[compoundDisposable removeDisposable:finishedDisposable];
}
};
void (^addSignal)(RACSignal *) = ^(RACSignal *signal) {
OSAtomicIncrement32Barrier(&signalCount);
RACSerialDisposable *selfDisposable = [[RACSerialDisposable alloc] init];
[compoundDisposable addDisposable:selfDisposable];
RACDisposable *disposable = [signal subscribeNext:^(id x) {
// MARK_3 这里的 subscriber 还是bind返回信号的 订阅者 所以这里会出来外面对bind信号的订阅者消息
[subscriber sendNext:x];
} error:^(NSError *error) {
[compoundDisposable dispose];
[subscriber sendError:error];
} completed:^{
@autoreleasepool {
completeSignal(selfDisposable);
}
}];
selfDisposable.disposable = disposable;
};
@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;
// MARK_2 包装后的信号
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的操作流程
- 调用bind方法,会生成一个新的信号。
- 对新的信号进行订阅,会触发bind传入的block,这时bind的内部实现会去订阅原信号
- 原信号发送后,会触发,bind内部对原信号的订阅者block。这时会调用bind是内部block返回的用来过滤信号的block(对应MAKR_1的block)。
- 这是将原数据发送到 MAKR_1 处的block中,获取到一个包装后的信号。(MARK_2 信号 RACReturnSignal 类型)
- 然后将 MARK_2 信号进行订阅。而 RACReturnSignal 类型的信号重写了
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
return [RACScheduler.subscriptionScheduler schedule:^{
[subscriber sendNext:self.value];
[subscriber sendCompleted];
}];
}
所以当信号订阅时,会立马触发nextBlock
- MARK_2 订阅者中调用 bind 信号订阅者去发送信号,所以外部对bind信号订阅的
nextBlock
这时就会调用(MARK_3) - 这是就完成了整个流程,bind后,原信号发送的消息,会经过bind时传入的block进行过滤,然后返回一个新的消息,发送的bind信号的。
原信号->bind(生成新信号)
原信号发送消息->bind Block 进行过滤,然后发送消息到内部一个信号中-> 转发到bind时生成的新信息号中
RAC中运用了很多hook的思想,所以会非常的绕,其实本质有点类似消息转发,将原信号进行包装过滤,在发送到一个新的信号中。
return [[self bind:^{
return ^(id value, BOOL *stop) {
id stream = block(value) ?: [class empty];
NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);
return stream;
};
}] setNameWithFormat:@"[%@] -flattenMap:", self.name];
Vertical Cross Functional Template.png还有一个很重要的
flattenMap
方法,其实就是bind
的封装。内部就是bind
方法的调用,然后可以传入一个block
作为过滤的规则