RACSignal 是RAC的核心,几乎所有的操作都是围绕着RACSignal类在执行的。这一章会讲解RACSignal类的创建和使用。如果读者对ReactiveCocoa的一些名词和架构不熟悉可以看上一篇ReactiveCocoa 基础.架构介绍
完成一个信号的生命周期大体分为四步
- 创建信号
- 订阅信号
- 发送信号
- 取消订阅
代码范例
1 创建Signal
RACSignal *testSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3 发送信号
[subscriber sendNext:@"土豆萝卜君"];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
//4 用于取消订阅清理资源 一般用来释放一些变量和对象 如果没有的话此处可以为nil
}];
}];
2订阅Next信号
[testSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
内部解析
1 创建信号
1.1 由上面的信号类使用图和代码可知,创建信号类方法中传入了一个返回值是RACDisposable 类型的实例变量,参数为id类型且遵守且遵守RACSubscriber协议的subscriber订阅者,名为didSubscribe的block 代码如下
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
return [RACDynamicSignal createSignal:didSubscribe];
}
1.2 类创建的是一个 RACDynamicSignal 类型的动态信号,并将 didSubscribe 传入
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy]; //重要方法 保存上一个传入的didSubscribe block
return [signal setNameWithFormat:@"+createSignal:"];
}
创建了一个 RACDynamicSignal 类型的信号,然后将传入的名为 didSubscribe 的block保存在创建的信号的 didSubscribe 属性中保存但是并未触发 (在有订阅者订阅此信号时候执行,此时只是一个冷信号)
2 订阅信号
订阅信号有subscribeNext, subscribeError, subscribeComlepted 三种方法。当信号被 订阅的时候此时冷信号变成了热信号就可以执行发送信号的操作。
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL); //如果nextBlock为空不在执行
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
//内部创建了RACSubscriber(订阅者)类的实例对象o,并且将nextBlock保存到o中,在返回值出执行o,实际也是执行了nextBlock。
return [self subscribe:o];
}
2.1 上面代码创建订阅者的实质是,创建一个订阅者,并保存相应的block 此时是保存并未触发![self subscribe:o]
方法才是最重要的 (RACDynamicSignal类中)
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
NSCParameterAssert(subscriber != nil);
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
if (self.didSubscribe != NULL) {
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
//在这里执行了在步骤1传入的 didSubscribe block 并返回一个RACDisposable类型的实例变量
[disposable addDisposable:innerDisposable];
}];
[disposable addDisposable:schedulingDisposable];
}
return disposable;
}
-
1 代码中传入了2.1中创建的RACSubscriber *o 订阅者,里面保存着 步骤1创建信号时的didSubscribe block
-
2 这里生成了一个RACCompoundDisposable类型的disposable 主要用来管理订阅结束以及资源的清理
-
3 RACPassthroughSubscriber 类型的订阅者主要是对传入的订阅者,当前的信号以及创建的disposable 进行一个包装,感觉作用就是做一个变量的统一入口,实际起作用的应该还是刚传入的订阅者
-
4 执行 didSubscribe block 将返回过来的innerDisposable 传入刚刚生成的disposable,也把调度器返回过来的schedulingDisposable 也保存到disposable 然后统一管理。当 RACCompoundDisposable 对象被 disposed 时,它会调用其所包含的所有 disposable 对象的 -dispose 方法从而做到统一管理
-
5 总结订阅信号本质就是创建了一个 RACPassthroughSubscriber 类型的订阅者,并将传入的代码块保存起来,留待以后调用,同时调用了第一步创建信号中保存的代码块,并传入创建的订阅者
3 发送信号
- (void)sendNext:(id)value {
if (self.disposable.disposed) return;
if (RACSIGNAL_NEXT_ENABLED()) {
RACSIGNAL_NEXT(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString([value description]));
}
[self.innerSubscriber sendNext:value];
}
- (void)sendError:(NSError *)error {
if (self.disposable.disposed) return;
if (RACSIGNAL_ERROR_ENABLED()) {
RACSIGNAL_ERROR(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description), cleanedDTraceString(error.description));
}
[self.innerSubscriber sendError:error];
}
- (void)sendCompleted {
if (self.disposable.disposed) return;
if (RACSIGNAL_COMPLETED_ENABLED()) {
RACSIGNAL_COMPLETED(cleanedSignalDescription(self.signal), cleanedDTraceString(self.innerSubscriber.description));
}
[self.innerSubscriber sendCompleted];
}
-
发送信号就是执行订阅时候传入的next error completed的block
-
对于 sendError 和 sendCompleted 都是先取消订阅,再执行相应的代码块,而 sendNext 并未使订阅结束,这样的话,对之后讨论的各种组合方法中必须写上 sendCompleted来结束订阅的做法就好理解了也就印证了上一篇的 "一个信号的生命周期是由任意个 next 事件和一个 error 事件或一个 completed 事件组成的"这句话。
4取消订阅
想要结束订阅只要将相应生成的disposable执行dispose即可。原因在于步骤2中的调度器
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable];
}];
- 首先我们要知道执行订阅的代码块实际上是放到相应的调度器中去执行,接下来点击方法中
- (RACDisposable *)schedule:(void (^)(void))block {
NSCParameterAssert(block != NULL);
if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block];
block();
return nil;
}
-
如果currentScheduler不为nil的时候代码块就不会放到调度器里去执行,而是直接执行,此时disposable的dispose方法就没有用了
-
如果currentScheduler为空的时候会执行
- (RACDisposable *)schedule:(void (^)(void))block {
NSCParameterAssert(block != NULL);
RACDisposable *disposable = [[RACDisposable alloc] init];
dispatch_async(self.queue, ^{
if (disposable.disposed) return;
[self performAsCurrentScheduler:block];
});
return disposable;
}
- 代码块未被调度的之前,生成的disposable被dispose的话,代码块就不会被执行。
总结:
- 步骤1 创建信号本质就是创建了一个 RACDynamicSignal类型的动态信号,并将传入block保存起来
- 步骤2 订阅信号本质就是创建了一个 RACPassthroughSubscriber 类型的订阅者,并将传入的nextBlock保存起来同时调用了第一步创建信号中保存的代码块,并传入创建的订阅者
- 步骤3 发送信号就是执行订阅信号时对应的block。
- 步骤4 取消订阅就是把订阅信号获得的disposable 进行dispose即可在调度器调度该部分之前直接返回不在继续执行达到取消的效果
网友评论