通过下面一张图理解RACSignal的调用过程:

创建signale

RACSignal通过子类[RACDynamicSignal createSignal:]方法获得Signal,并将disSubscribe这个block保存在Signal中。

+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
return [RACDynamicSignal createSignal:didSubscribe];
}
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe {
RACDynamicSignal *signal = [[self alloc] init];
signal->_didSubscribe = [didSubscribe copy];
return [signal setNameWithFormat:@"+createSignal:"];
}

创建subscriber

signal通过调用subscribeNext方法生成subscriber,并将next、error、completed block保存在subscriber中

- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
NSCParameterAssert(nextBlock != NULL); RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
return [self subscribe:o];
}
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
RACSubscriber *subscriber = [[self alloc] init]; subscriber->_next = [next copy];
subscriber->_error = [error copy];
subscriber->_completed = [completed copy]; return subscriber;
}

进行subscribe

第二步创建subscriber之后调用signal的subscribe方法,并将创建的subscriber作为参数。

这一步会生成RACCompoundDisposable和RACPassthroughSubscriber对象。

  • RACCompoundDisposable:RACDisposable的子类,可以加入多个RACDisposable对象。当RACCompoundDisposable对象被dispose的时候,会dispose容器内的所有RACDisposable对象。
  • RACPassthroughSubscriber:分别保存对RACSignal,RACSubscriber,RACCompoundDisposable的引用。通过RACPassthroughSubscriber对象来转发给真正的Subscriber。
- (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);
[disposable addDisposable:innerDisposable];
}]; [disposable addDisposable:schedulingDisposable];
} return disposable;
}

执行disSubscribe block

RACSignal通过RACScheduler.subscriptionScheduler来执行闭包,disSubscribe真正被调用的的位置就是上一步的RACDisposable *innerDisposable = self.didSubscribe(subscriber);

- (RACDisposable *)schedule:(void (^)(void))block {
NSCParameterAssert(block != NULL); if (RACScheduler.currentScheduler == nil) return [self.backgroundScheduler schedule:block]; block();
return nil;
}

调用sendNext sendError sendCompleted

进入didSubscribe闭包后,调用sendNext:、sendError:、sendCompleted。由于第三步中将subscriber替换为RACPassthroughSubscriber对象,真正的subscriber被存储在RACPassthroughSubscriber对象中,即innerSubscriber,所以这一步的各种send方法其实是一个转发过程。

- (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闭包

通过调用innerSubscriber的sendNext:、sendError、和sendCompleted方法执行真正的subscriber中的next error completed闭包

- (void)sendNext:(id)value {
@synchronized (self) {
void (^nextBlock)(id) = [self.next copy];
if (nextBlock == nil) return; nextBlock(value);
}
} - (void)sendError:(NSError *)e {
@synchronized (self) {
void (^errorBlock)(NSError *) = [self.error copy];
[self.disposable dispose]; if (errorBlock == nil) return;
errorBlock(e);
}
} - (void)sendCompleted {
@synchronized (self) {
void (^completedBlock)(void) = [self.completed copy];
[self.disposable dispose]; if (completedBlock == nil) return;
completedBlock();
}
}

过程回顾

去掉中间的繁杂细节,大致过程如下:

1.通过createSignal生成信号

2.通过subscribeNext确定信号内容到来时的处理方式

3.didSubscribe block块中异步处理完毕之后,进行sendNext、sendError和sendCompleted自动处理

一张图理解RACSignal的Subscription过程的更多相关文章

  1. 一张图搞清楚PMBOK所有过程的使用

      很多参加PMP培训的学员大概都会有一个感受,上课时似乎每个知识点都听懂了,大的知识框架也弄明白了,但是所有这些串起来在实践中怎么用呀!说的再直接一点,在考试的时候这些过程和活动是以怎样的逻辑来应用 ...

  2. 深入理解javascript作用域系列第五篇——一张图理解执行环境和作用域

    × 目录 [1]图示 [2]概念 [3]说明[4]总结 前面的话 对于执行环境(execution context)和作用域(scope)并不容易区分,甚至很多人认为它们就是一回事,只是高程和犀牛书关 ...

  3. 几张图理解Roll, Pitch, Yaw的含义

    Roll:翻滚    Pitch:俯仰    Yaw:偏航 有时候不知道它到底绕着哪个轴旋转得到的角,一个比较容易的记法是根据字母的排列顺序PRY分别对应XYZ轴进行旋转得到的角,即: Pitch是绕 ...

  4. 一张图理解prototype、proto和constructor的三角关系

    × 目录 [1]图示 [2]概念 [3]说明[4]总结 前面的话 javascript里的关系又多又乱.作用域链是一种单向的链式关系,还算简单清晰:this机制的调用关系,稍微有些复杂:而关于原型,则 ...

  5. 8张图理解Java

    一图胜千言,下面图解均来自Program Creek 网站的Java教程,目前它们拥有最多的票选.如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟. 1.字符串不变性 下面这张图展示了这段代码做 ...

  6. 【转】8张图理解Java

    一图胜千言,下面图解均来自Program Creek 网站的Java教程,目前它们拥有最多的票选.如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟. 1.字符串不变性 下面这张图展示了这段代码做 ...

  7. [ImportNew]8张图理解Java

    http://www.importnew.com/11725.html 1.字符串的不变性. 下面这张图展示了这段代码做了什么 String s = "abcd"; s = s.c ...

  8. 8张图理解Java(转)

    一图胜千言,下面图解均来自Program Creek 网站的Java教程,目前它们拥有最多的票选.如果图解没有阐明问题,那么你可以借助它的标题来一窥究竟. 1.字符串不变性 下面这张图展示了这段代码做 ...

  9. [转] 一张图理解prototype、proto和constructor的三角关系

    前面的话 javascript里的关系又多又乱.作用域链是一种单向的链式关系,还算简单清晰:this机制的调用关系,稍微有些复杂:而关于原型,则是prototype.proto和constructor ...

随机推荐

  1. ZOJ 1108 & HDU 1160 - FatMouse's Speed

    题目大意:给你n只老鼠的体重w和速度s,让你找出最长的子序列使得w[i] < w[j] 且 s[i] > s[j] (i < j).求最长序列的长度并输出该序列. LIS(Longe ...

  2. iPhone的刷机 iPhone进UDF

    首先是进入udf出现问题.. 要点是 黑屏后按电源键到屏幕亮了+HOME键再一直按 按到黑屏 4秒左右后 放开HOME键 再按大概7秒就是udf界面了 不是界面~全黑的.你可以电源键是开不了机的. 然 ...

  3. osgearth earth文件规范-符号参考

    osgearth earth文件规范-符号参考 osgEarth用样式表渲染要素和注记. 本文档列出了可在样式表中使用的所有符号属性.不是每个符号是适用于每种情况:这只是一个主列表. 跳转到符号: • ...

  4. easyui datagrid 列排序

    1.js设置 //=====================数据加载===================== /** * grid加载数据 * * @returns */ function grid ...

  5. JDBC连接SQL Server 2005步骤详解

    一.设置SQL Server服务器:    1.“开始” → “程序” → “Microsoft SQL Server 2005” → “配置工具” → “SQL Server Configurati ...

  6. Unity3D在NGUI中使用mask

    过程是这样的:最近一直想做一个头像的mask效果,后来发现原来unity的mask需要用shader来写,网上找了不少资料,也能实现,不过大多数都是用render texture作为相机投影的text ...

  7. 实现过程全纪录——自己写一个“微信朋友圈”(包括移动端与PC端)

    一.朋友圈的基本单元--动态 首先定义一个自定义控件用来显示每条动态. 二.运行效果 三.核心解读 PushedMessage 有个PushIndex属性,表示发送消息的index,从0开始递增.每个 ...

  8. vue-router跳转页面

    小结放在前:先祝大家新年快乐!鸡年大吉大利!在新的一年里大家都有跳跃般的成长!作为新年的第一篇文章,就拿他来给大家拜个年!!!文章前部份讲解了vue-router路由的配置,后半部分为去年的文章vue ...

  9. C语言函数名与函数指针详解

    一.通常的函数调用 一个通常的函数调用的例子: /* 自行包含头文件 */ void MyFun(int x); /* 此处的声明也可写成:void MyFun(int) */ int main(in ...

  10. MySQL 存储表情字符

    摘要 在 MySQL 中直接存储表情的时候,会出现无法插入数据的错误. 这是由于一般情况下,MySQL 的字符集是 utf8,而对于 emoji 表情的 mysql 的 utf8 字符集是不支持,需要 ...