A co-worker recently asked me about the difference between -replay, -replayLast, and -replayLazily in the ReactiveCocoa library. I had a vague understanding of the three but was not able to confidently explain the difference, so I thought I would look into it further.

I found the header documentation to be difficult to understand if you don’t have a good understanding of RACReplaySubject and RACMulticastConnection, so I’m going to try to explain the replay methods without getting into those underlying concepts.

Subscribing to a Signal

With a “normal” RACSignal each subscription to the signal causes the subscription code to be executed again, and the subscriber only receives values that are sent after the subscription is made. I think it is easiest to show this in two different examples.

The first example shows how the subscription code gets re-executed on each subscription.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  __block int num = 0;
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
num++;
NSLog(@"Increment num to: %i", num);
[subscriber sendNext:@(num)];
return nil;
}];
 
NSLog(@"Start subscriptions");
 
// Subscriber 1 (S1)
[signal subscribeNext:^(id x) {
NSLog(@"S1: %@", x);
}];
 
// Subscriber 2 (S2)
[signal subscribeNext:^(id x) {
NSLog(@"S2: %@", x);
}];
 
// Subscriber 3 (S3)
[signal subscribeNext:^(id x) {
NSLog(@"S3: %@", x);
}];

Running this example will produce:

1
2
3
4
5
6
7
  Start subscriptions
Increment num to: 1
S1: 1
Increment num to: 2
S2: 2
Increment num to: 3
S3: 3

As you can see, each subscription causes num to be incremented. Also note that num is not incremented until a subscription is made. In this way, a normal RACSignal can be thought of as lazy, as it doesn’t do any work until it has a subscriber.

Our second example shows how each subscriber only receives the values that are sent after their subscription is added.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  RACSubject *letters = [RACSubject subject];
RACSignal *signal = letters;
 
NSLog(@"Subscribe S1");
[signal subscribeNext:^(id x) {
NSLog(@"S1: %@", x);
}];
 
NSLog(@"Send A");
[letters sendNext:@"A"];
NSLog(@"Send B");
[letters sendNext:@"B"];
 
NSLog(@"Subscribe S2");
[signal subscribeNext:^(id x) {
NSLog(@"S2: %@", x);
}];
 
NSLog(@"Send C");
[letters sendNext:@"C"];
NSLog(@"Send D");
[letters sendNext:@"D"];
 
NSLog(@"Subscribe S3");
[signal subscribeNext:^(id x) {
NSLog(@"S3: %@", x);
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Subscribe S1
 
Send A
S1: A
 
Send B
S1: B
 
Subscribe S2
 
Send C
S1: C
S2: C
 
Send D
S1: D
S2: D
 
Subscribe S3

In many cases this is the desired behavior. But in some cases, you don’t want the subscription code to be re-executed, e.g. a subscription makes a request to a web service and there are multiple listeners that need to be updated when the result comes in. Or maybe you want to get the history of values that have been sent on the signal prior to a subscription. This is where -replay, -replayLast, and -replayLazily can be used.

Subscribing to a -replay Signal

The -replay convenience method returns a new signal that, when subscribed to, will immediately send the subscriber the entire history of values that have come through the source signal, without re-executing the source signal’s subscription code. The subscriber will still receive all future values from the signal just as it would from a normal signal.

The first example shows how the subscription code is not re-executed upon new subscriptions:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  __block int num = 0;
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
num++;
NSLog(@"Increment num to: %i", num);
[subscriber sendNext:@(num)];
return nil;
}] replay];
 
NSLog(@"Start subscriptions");
 
// Subscriber 1 (S1)
[signal subscribeNext:^(id x) {
NSLog(@"S1: %@", x);
}];
 
// Subscriber 2 (S2)
[signal subscribeNext:^(id x) {
NSLog(@"S2: %@", x);
}];
 
// Subscriber 3 (S3)
[signal subscribeNext:^(id x) {
NSLog(@"S3: %@", x);
}];
1
2
3
4
5
  Increment num to: 1
Start subscriptions
S1: 1
S2: 1
S3: 1

This time the num integer is incremented immediately, before there are even any subscribers. And it is only incremented once, meaning that the subscription code is only been executed a single time, regardless of how many subscribers the signal has.

The second example shows how each new subscriber receives the full history of the signal:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  RACSubject *letters = [RACSubject subject];
RACSignal *signal = [letters replay];
 
NSLog(@"Subscribe S1");
[signal subscribeNext:^(id x) {
NSLog(@"S1: %@", x);
}];
 
NSLog(@"Send A");
[letters sendNext:@"A"];
NSLog(@"Send B");
[letters sendNext:@"B"];
 
NSLog(@"Subscribe S2");
[signal subscribeNext:^(id x) {
NSLog(@"S2: %@", x);
}];
 
NSLog(@"Send C");
[letters sendNext:@"C"];
NSLog(@"Send D");
[letters sendNext:@"D"];
 
NSLog(@"Subscribe S3");
[signal subscribeNext:^(id x) {
NSLog(@"S3: %@", x);
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Subscribe S1
 
Send A
S1: A
 
Send B
S1: B
 
Subscribe S2
S2: A
S2: B
 
Send C
S1: C
S2: C
 
Send D
S1: D
S2: D
 
Subscribe S3
S3: A
S3: B
S3: C
S3: D

Even though S3 subscribed after all of the values had been sent, it still received all of the values.

Subscribing to a -replayLast Signal

The -replayLast convenience method returns a new signal that, when subscribed to, will immediately send the subscriber the most recent value that comes through the source signal, without re-executing the source signal’s subscription code. The subscriber will then receive all future values from the signal just as it would from a normal signal.

For the first example, there is no difference between -replayLast and -replay so I won’t bother showing it again.

The second example illustrates how only the most recent value is provided to a new subscriber.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  RACSubject *letters = [RACSubject subject];
RACSignal *signal = [letters replayLast];
 
NSLog(@"Subscribe S1");
[signal subscribeNext:^(id x) {
NSLog(@"S1: %@", x);
}];
 
NSLog(@"Send A");
[letters sendNext:@"A"];
NSLog(@"Send B");
[letters sendNext:@"B"];
 
NSLog(@"Subscribe S2");
[signal subscribeNext:^(id x) {
NSLog(@"S2: %@", x);
}];
 
NSLog(@"Send C");
[letters sendNext:@"C"];
NSLog(@"Send D");
[letters sendNext:@"D"];
 
NSLog(@"Subscribe S3");
[signal subscribeNext:^(id x) {
NSLog(@"S3: %@", x);
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Subscribe S1
 
Send A
S1: A
 
Send B
S1: B
 
Subscribe S2
S2: B
 
Send C
S1: C
S2: C
 
Send D
S1: D
S2: D
 
Subscribe S3
S3: D

Subscribing to a -replayLazily Signal

The -replayLazily convenience method returns a new signal that, when subscribed to, will immediately send the subscriber the entire history of values that have come through the source signal, without re-executing the source signal’s subscription code. The difference between -replayLazily and -replay is that -replayLazily will not subscribe to the source signal until something subscribes to the newly created signal. This is opposed to the behavior of -replay and -replayLast, which subscribe to the source signal immediately upon being called.

The first example illustrates this difference. Note how the “Increment num to: 1” message does not appear until after the first subscription. With -replay (and -replayLast) the same message appears before the first subscription.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
  __block int num = 0;
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
num++;
NSLog(@"Increment num to: %i", num);
[subscriber sendNext:@(num)];
return nil;
}] replayLazily];
 
NSLog(@"Start subscriptions");
 
// Subscriber 1 (S1)
[signal subscribeNext:^(id x) {
NSLog(@"S1: %@", x);
}];
 
// Subscriber 2 (S2)
[signal subscribeNext:^(id x) {
NSLog(@"S2: %@", x);
}];
 
// Subscriber 3 (S3)
[signal subscribeNext:^(id x) {
NSLog(@"S3: %@", x);
}];
1
2
3
4
5
Start subscriptions
Increment num to: 1
S1: 1
S2: 1
S3: 1

And the second example shows that the full history is sent to any new subscribers, just like with -replay.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  RACSubject *letters = [RACSubject subject];
RACSignal *signal = [letters replayLazily];
 
NSLog(@"Subscribe S1");
[signal subscribeNext:^(id x) {
NSLog(@"S1: %@", x);
}];
 
NSLog(@"Send A");
[letters sendNext:@"A"];
NSLog(@"Send B");
[letters sendNext:@"B"];
 
NSLog(@"Subscribe S2");
[signal subscribeNext:^(id x) {
NSLog(@"S2: %@", x);
}];
 
NSLog(@"Send C");
[letters sendNext:@"C"];
NSLog(@"Send D");
[letters sendNext:@"D"];
 
NSLog(@"Subscribe S3");
[signal subscribeNext:^(id x) {
NSLog(@"S3: %@", x);
}];
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Subscribe S1
 
Send A
S1: A
 
Send B
S1: B
 
Subscribe S2
S2: A
S2: B
 
Send C
S1: C
S2: C
 
Send D
S1: D
S2: D
 
Subscribe S3
S3: A
S3: B
S3: C
S3: D

Summary

ReactiveCocoa provides three convenience methods for allowing multiple subscribers to the same signal, without re-executing the source signal’s subscription code, and to provide some level of historical values to later subscribers. -replay and -replayLast both make the signal hot, and will provide either all values (-replay) or the most recent (-replayLast) value to subscribers. -replayLazily returns a cold signal that will provide all of the signal’s values to subscribers.

转:RAC中比较replay, replayLast, and replayLazily的更多相关文章

  1. ReactiveCocoa比较区分replay, replayLast和replayLazily

    一直搞不清楚replayLazily和replay的区别可以直接跳到最后看. 原文:http://spin.atomicobject.com/2014/06/29/replay-replaylast- ...

  2. Oracle 10g RAC中的DRM问题及关闭

    在RAC环境中,Oracle使用GRD(Global Resource Service)来记录各个RAC节点的资源信息,具体通过GCS(Global Cache Service)和GES(Global ...

  3. rac中 kull session会话脚本

    方法:ALTER SYSTEM KILL SESSION '80, 6, @2';  --<= 80 sid,6 serial#,@2 inst_id kill session 脚本如下:sel ...

  4. 这里的*号实际表示就是RAC中所有实例都使用

    您的位置: ITPUB个人空间 » cc59的个人空间 » 日志 发布新日志 我的日志我的足迹我的收藏 unix/linuxHA随笔backup&restoreperformance tuni ...

  5. 使用OpenFiler来模拟存储配置RAC中ASM共享盘及多路径(multipath)的测试

    第一章 本篇总览 之前发布了一篇<Oracle_lhr_RAC 12cR1安装>,但是其中的存储并没有使用多路径,而是使用了VMware自身提供的存储.所以,年前最后一件事就是把多路径学习 ...

  6. 详解 RAC 中各种IP和监听的意义

    一.SCAN 概念 SCAN(Single Client Access Name)是 Oracle从11g R2开始推出的,客户端可以通过 SCAN 特性负载均衡地连接到 RAC数据库 SCAN 最明 ...

  7. 关于Oracle RAC中SCN原理和机制的探索

    今天看书时看到了关于RAC中SCN的问题,为了进一步搞清楚其内部原理和机制,对该问题进行了广泛的查阅和搜索,遗憾的是,可以参考的资料很少,网上大部分是人云亦云的帖子,其中,详细介绍其内部原理和机制的资 ...

  8. Oracle 11G R2 RAC中的scan ip 的用途和基本原理【转】

    Oracle 11G R2 RAC增加了scan ip功能,在11.2之前,client链接数据库的时候要用vip,假如你的cluster有4个节点,那么客户端的tnsnames.ora中就对应有四个 ...

  9. 转 rac中并行 PARALLEL 的设置

    sample 1: rac中并 行的设置 https://blog.csdn.net/wll_1017/article/details/8285574 我们的生产库一般在节点一上的压力比较大,在节点二 ...

随机推荐

  1. 冲刺One之站立会议8 /2015-5-21

    今天我们把聊天界面做了优化和改进,主要实现了聊天的功能.显示了正在进行通信的成员列表,和当前状态,是否连通和正常通信,大体完成了预期的目标. 燃尽图8

  2. 初学Cocos2dx

    初学cocos2dx Cocos2dx 中的主要概念包括:应用.导演.场景.层.精灵.动画.动作. Cocos2dx里面的主要类 1.CCObject Object Object Object 是co ...

  3. Task2 四则运算2

    1.任务要求:对之前的自动出题系统提出了新的要求:(1).题目避免重复:(2).可定制(数量/打印方式):(3)可以控制下列参数:是否有乘除法.数值范围.加减有无负数.除法有无余数.是否支持分数... ...

  4. 项目冲刺Beta第一篇博客

    Beta版本冲刺计划安排 1.当天站立式会议照片: 2.工作分工: 团队成员 分工 张洪滨060  排行榜界面美化 陈敬轩059  注册成功界面美化 黄兴067  登录界面美化 林国梽068  答题界 ...

  5. boolean类型的按位或||和|的区别

    boolean类型既可以使用&&和||做逻辑运算,也可以使用&和|做逻辑运算,但前者是经过优化的(执行短路运算),后者未优化. 以下代码验证: 逻辑或|| public cla ...

  6. Java 经典 书籍

    1.<你的灯还亮着么> 方法论 2.<程序员修炼之道 从小工到专家> 方法论 3.<发布!软件的设计与部署> 案例&经验总结 4.<思考,快与慢> ...

  7. 初识asp

    1.ASP(Active Server Pages 动态服务器页面)是一种生成动态交互性网页的强有力工具 <!DOCTYPE html> <html> <body> ...

  8. 新手向:Vue 2.0 的建议学习顺序

    新手向:Vue 2.0 的建议学习顺序 尤雨溪   1 年前 注:2.0 已经有中文文档 .如果对自己英文有信心,也可以直接阅读英文文档.此指南仅供参考,请根据自身实际情况灵活调整.欢迎转载,请注明出 ...

  9. Ubuntu下面 PHPSTORM2017.2破解方法

    Ubuntu下面 PHPSTORM2017.2破解方法 下载破解文件 在 http://idea.lanyus.com/上面新下载一个破解文件. 破解步骤 将JetbrainsCrack-2.6.3_ ...

  10. 对比数据库字段不同的sql (mysql版)

    -- 使用test库 `test_project_management` `oel_project_management` USE test; -- 旧表 DROP TABLE old_column_ ...