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. C#中周,月,第几周,周开始结束时间de方法总结

    1.c#获取当前时间是本年的第几周,本月的第几周 private static int getWeekNumInMonth(DateTime daytime) { int dayInMonth = d ...

  2. HDU 1170 Shopping Offers 离散+状态压缩+完全背包

    题目链接: http://poj.org/problem?id=1170 Shopping Offers Time Limit: 1000MSMemory Limit: 10000K 问题描述 In ...

  3. nodeJS + webStrome

    一.配置开发环境: 1.先安装node (1).访问http://nodejs.org打开安装包,正常安装,点击next即可. 为了测试是否安装成功,打开命令提示符,输入node,则进入node.js ...

  4. WebGL学习笔记(二)

    目录 绘制多个顶点 使用缓冲区对象 类型化数组 使用drawArrays()函数绘制图形 图形的移动 图形的旋转 图形的缩放 绘制多个顶点 使用缓冲区对象 创建缓冲区对象 var vertexBuff ...

  5. week4f:个人博客作业

    8,工作中的照片 9,对方编程习惯总结 宋成鑫(以后简称老宋).老宋,对编程的思想看的比较重,具体什么是编程的思想,我是也不是很清楚.但是,在编程过程中,老宋的一些话给了我启示.这或许就是编程的思想吧 ...

  6. Java& Vector与ArrayList区别

    在写java的时候,基本上都喜欢用arraylist,甚至我都不知道有个vector的存在.查了一下发现又是线程安全问题...咋个线程安全天天围着我转呢...多得阿里巴巴,让我开始认识java的所谓线 ...

  7. 第六周PSP&进度条

    团队项目PSP 一.表格:     C类型 C内容 S开始时间 E结束时间 I时间间隔 T净时间(mins) 预计花费时间(mins) 讨论 讨论alpha完成情况并总结 9:40 11:20 17 ...

  8. Python入门:学会创建并调用函数

    这是关于Python的第4篇文章,主要介绍下如何创建并调用函数. print():是打印放入对象的函数 len():是返回对象长度的函数 input():是让用户输入对象的函数 ... 简单来说,函数 ...

  9. Python2X和Python3X的区别

    python2X:源码重复不规范python3X:整合源码,更清晰简单优美.         python2X:默认的编码是ascii  (解决办法为第一行添加 :  #-*- encoding:ut ...

  10. SpringBoot(八)_springboot集成swagger2

    swagger是一个功能强大的api框架,它的集成非常简单,不仅提供了在线文档的查阅,而且还提供了在线文档的测试. (1) 引入依赖,我们选择现在最新的版本 <dependency> &l ...