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. Hibernate笔记②--hibernate类生成表、id生成策略、级联设置、继承映射

    一.多表的一个关联关系 老师和学生是一对多的关系 student:tid属性 外键约束 对应teacher表中的id属性 teacher:id 在myeclipse的db窗口中选中两个表来生成类.   ...

  2. 团队作业4--第一次项目冲刺2(Alpha版本)

    1.会议 第二次会议: ①:总结第一天任务出现的问题 ②:安排下面两天的任务 2任务安排 3.任务分解图 4.燃尽图 5.适当的项目程序/模块的最新(运行)截图 6.心得 因为做前端的同学并不擅长这方 ...

  3. SQL语句中的output用法

    private void button2_Click(object sender, RoutedEventArgs e) { using (SqlConnection conn = new SqlCo ...

  4. Linux操作系统(一)

    操作系统:介于硬件与用户之间的一组程序,方便用户操作,用以管理计算机的所有活动及硬件资源. 1.硬件->内核->系统调用(shell.命令)->应用程序. 只要具备以下几点,即可称为 ...

  5. scp命令与Screen服务的区别

    scp:远程传输命令.(通过网络传送给其他主机,又恰好两台主机都是linux系统,便可以使用scp传输文件) 参数 作痛 -v 先是详细的连接进度 -P 指定远程主机的sshd端口号 -r 传送文件夹 ...

  6. contos7忘记root密码怎么办

    首先在这个界面按"e"键 然后呢就会进入到如下图所示的界面,在LANG=zh_CN.UTF8的后面加上 init=/bin/sh, 再按 [ Ctrl + X ] 进入'单用户模式 ...

  7. 初期测评 E 迷障

    https://vjudge.net/contest/240302#problem/E 通过悬崖的yifenfei,又面临着幽谷的考验—— 幽谷周围瘴气弥漫,静的可怕,隐约可见地上堆满了骷髅.由于此处 ...

  8. Mysql-外键foreign key

    1.定义:如果一张表中有一个字段指向另一张表的主键,就子表中将该主键字段叫做外键. 一张表中可存在多个外键 2.外键的作用 保持数据的一致性.完整性 a.对子表(外键所在的表)的作用:子表在进行写操作 ...

  9. iOS 数组和字典排序

    一.数组排序 数组排序方式1: //初始化可变数组 NSMutableArray *arr1=[NSMutableArray arrayWithObjects:@"giu",@&q ...

  10. BZOJ5125 小Q的书架(决策单调性+动态规划+分治+树状数组)

    设f[i][j]为前i个划成j段的最小代价,枚举上个划分点转移.容易想到这个dp有决策单调性,感性证明一下比较显然.如果用单调栈维护决策就不太能快速的求出逆序对个数了,改为使用分治,移动端点时树状数组 ...