一直搞不清楚replayLazily和replay的区别可以直接跳到最后看。

 原文:http://spin.atomicobject.com/2014/06/29/replay-replaylast-replaylazily/

  

  如果没有对RACReplaySubject和RACMulticastConnection深入研究的话,你将会很难理解它们在头文件中的描述。现在我们不去了解底层原理,用通俗的语言和图表去描述解析这几个方法。

Subscribing to a Signal

  对于一个“普通”的信号,每次订阅都将会导致信号中的代码再执行一遍,且该次订阅者仅接收到该次订阅发送出去的值。

  第一个例子演示每次订阅都会重新执行订阅代码。

__block int num = ;
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);
}];

  运行结果如下:

Start subscriptions
Increment num to:
S1:
Increment num to:
S2:
Increment num to:
S3:

  可以看到,每次订阅num都在递增,如果不订阅则不会递增。通过这种方式,可以知道信号是懒惰的,如果没有订阅者的话,是不会执行的。

  第二个例子演示信号被添加订阅的时候,订阅者是怎么接收发送的值的。

 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);
}];

运行结果

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

  在很多情况下,这是我们想要的预期结果,不过在某些情况下,你不需要订阅的代码再次被执行。例如订阅 一个向网络服务器发送的请求,当服务器返回数据时,多个监听者需要更新(无论有多少个监听者,请求只发送一下(第一个例子就不满足我们的需求)),或者我们想拿到订阅前信号发送过的值(第二个例子,S2想拿A,B的值或者S3想拿A,B,C,D的值,就不满足我们的需求了)。因此-replay, -replayLast, and -replayLazily应需而生。

Subscribing to a -replay Signal

  这个replay方法将返回一个新的信号,当源信号被订阅时,会立即发送给订阅者全部历史的值,不会重复执行源信号中的订阅代码,不仅如此,订阅者还将收到所有未来发送过去的值。

  第一个例子演示信号添加新的订阅时,代码是不会再次被执行的。

__block int num = ;
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);
}];
Increment num to:
Start subscriptions
S1:
S2:
S3:

  信号首次被订阅时,num立马被递增了,且仅仅递增了一次。这说明了不管有你多个订阅者,订阅代码我只执行了一次。

  

  第二个例子演示每个新添加的订阅者接收到信号中全部的值(不管是之前发出的值还是将来发出的值)。

  

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);
}];
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

  尽管订阅者S3在所有的值发送之后再订阅,然后还能接收到所有的值。

Subscribing to a -replayLast Signal

  这个replayLast返回一个新的信号,当源信号被订阅时,会立即发送给订阅者最新的值,不会重复执行源信号中的订阅代码。订阅者还会收到信号未来所有的值。

  对于第一个例子,跟之前replay一样,所以我就不再次演示了。

  第二个例子演示如何将最新的值提供给新的订阅者

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);
}];
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

  这replayLazily方法返回一个新的信号,当源信号被订阅时,会立即发送给订阅者全部历史的值,不会重复执行源信号中的订阅代码。跟replay不同的是,replayLazily被订阅生成新的信号之前是不会对源信号进行订阅的(原文写的有点绕,简单来讲 直到订阅时候才真正创建一个信号,源信号的订阅代码才开始执行)。暂时不理解也没事,看下面的代码输出,和注释。

 这第一个例子会说明跟replay差异。 注意字符串 “Increment num to: 1”是被订阅了之后才打印显示的。而replay和replayLast没被订阅前就打印了“Increment num to: 1” 这个消息。

__block int num = ;
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
num++;
NSLog(@"Increment num to: %i", num);
[subscriber sendNext:@(num)];
return nil;
}] replayLazily]; //跟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);
}];
// 帖子滚动起来,跟replay比较一下 Increment num to: 1 的显示顺序。

Start subscriptions     // 实际订阅
Increment num to: // 信号开始创建
S1:
S2:
S3:

  第二个例子演示将全部历史的值提供给任何新的订阅者,就像replay一样。

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);
}];
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

  总结一下:

  ReactiveCocoa提供了这三个简便的方法允许多个订阅者订阅一个信号,却不会重复执行订阅代码,并且能给新加的订阅者提供订阅前的值。replay和replayLast使信号变成热信号,且会提供所有值(-replay) 或者最新的值(-replayLast) 给订阅者。 replayLazily返回一个冷的信号,会提供所有的值给订阅者。

ReactiveCocoa比较区分replay, replayLast和replayLazily的更多相关文章

  1. 转:RAC中比较replay, replayLast, and replayLazily

    A co-worker recently asked me about the difference between -replay, -replayLast, and -replayLazily i ...

  2. 一次MVVM+ReactiveCocoa实践

    前言 学习MVVM和ReactiveCocoa(简称RAC)也有一段时间了,不过都仅限于看博客,一直对这两个东西很感兴趣,觉得很创新,也一直想找个机会在项目中实践一下,但是还是有一些顾虑,毕竟没有实践 ...

  3. ReactiveCocoa v2.5 源码解析 之 架构总览

    ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,它受 Functional Reactive Programming 的启发,是 Justin Spahr-Summers 和 J ...

  4. ReactiveCocoa的冷信号与热信号 探讨

    背景 ReactiveCocoa(简称RAC)是最初由GitHub团队开发的一套基于Cocoa的FRP框架.FRP即Functional Reactive Programming(函数式响应式编程), ...

  5. RACSignal的Subscription深入

    ReactiveCocoa是一个FRP的思想在Objective-C中的实现框架,目前在美团的项目中被广泛使用.对于ReactiveCocoa的基本用法,网上有很多相关的资料,本文不再讨论.RACSi ...

  6. ReactiveCocoa_v2.5 源码解析之架构总览

    ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,它受 Functional Reactive Programming 的启发,是 Justin Spahr-Summers 和 J ...

  7. 走进ReactiveCocoa的世界

    在学习ReactiveCocoa之前,先学习一下概念 ReactiveCocoa 是一套开源的基于Cocoa的FRP框架 .FRP的全称是Functional Reactive Programming ...

  8. ReactiveCocoa基础知识内容

    本文记录一些关于学习ReactiveCocoa基础知识内容,对于ReactiveCocoa相关的概念如果不了解可以网上搜索:RACSignal有很多方法可以来订阅不同的事件类型,ReactiveCoc ...

  9. ReactiveCocoa应用篇(二)

    上一篇介绍了ReactiveCocoa的常用类,已经基本满足项目中的简单应用要求,但是针对复杂的功能还需要其它的类来协同处理.ReactiveCocoa提供了强大的流程处理功能来解决复杂的问题,包括事 ...

随机推荐

  1. C#中正则表达式进行忽略大小写的字符串替换

    在C#里要进行忽略大小写的字符串替换,用string的Replace是很难做到的,即使花了天大的力气做到了,效率仍然是很低的,正确的方法应该是使用正则表达式. 要使用正则表达式,首先需要引用命名空间: ...

  2. cocos2d-x 3.1.1 学习笔记[3]Action 动作

    这些动画貌似都非常多的样子,就所有都创建一次. 代码例如以下: /* 动画*/ auto sp = Sprite::create("card_bg_big_26.jpg"); Si ...

  3. 【JavaScript】父子页面之间跨域通信的方法

    由于同源策略的限制,JavaScript跨域的问题,一直是一个比较棘手的问题,为了解决页面之间的跨域通信,大家煞费苦心,研究了各种跨域方案.之前也有小网同学分享过一篇“跨域,不再纠结” 开始照着尝试时 ...

  4. Citrix 服务器虚拟化之二十八 XenApp6.5发布文档内容

    Citrix 服务器虚拟化之二十八  XenApp 6.5发布文档内容 XenApp可发布以下类型的资源向用户提供信息访问,这些资源可在服务器或桌面上虚拟化: 1)  服务器桌面:发布场中服务器的整个 ...

  5. [Angular2 Router] Use Params from Angular 2 Routes Inside of Components

    Angular 2’s ActivatedRoute allows you to get the details of the current route into your components. ...

  6. OpenRisc-67-OR的汇编

    引言 之前我们写过OR的裸机程序,写过基于OR的linux设备驱动程序,也反汇编过OR的机器码. 本小节,我们将通过一个简单的实验,对OR的汇编(指令集)做一个简单的梳理和測试. 1,基本思想 要想了 ...

  7. Android消息机制——时钟显示和异步处理工具类(AsyncTask)

    1. 时钟显示 定义布局文件——activity_my_analog_clock_thread_demo.xml <?xml version="1.0" encoding=& ...

  8. 进程控制之wait和waitpid函数

    当一个进程正常或异常终止时,内核就向其父进程发送SIGCHLD信号.因为子进程终止是个异步事件(这可以在父进程运行的任何时候发生),所以这种信号也是内核向父进程发的异步通知.父进程可以选择忽略该信号, ...

  9. linux安装qwt插件linux

    Optional: Get, Compile, Install QWT 5.0.0 (or newer): * NOTE: You should not need to set the environ ...

  10. paip.android 手机输入法制造大法

    paip.android 手机输入法制造大法 作者Attilax ,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http://blog.csdn.net/at ...