一直搞不清楚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. [AngularJS] Directive Definition Objects (DDO)

    This function that we just set up is what's called a link function, and it's actually a very small p ...

  2. input text框和 checkbox 连带被选中的情况

    <html> <head></head> <body> <ul> <li><input type="checkb ...

  3. MPI编程简单介绍

    第三章MPI编程 3.1 MPI简单介绍 多线程是一种便捷的模型,当中每一个线程都能够訪问其他线程的存储空间.因此,这样的模型仅仅能在共享存储系统之间移植.一般来讲,并行机不一定在各处理器之间共享存储 ...

  4. Browser 與 Server 持續同步的作法介紹 (Polling, Comet, Long Polling, WebSocket)长连接

    對 Comet 的懵懂 記得兩年多前,第一次看到 Gmail 中的 GTalk 覺得很好奇:「咦?線上聊天且是 Google 的熱門系統,只用傳統的 AJAX 應該會操爆伺服器吧?」很幸運的,當時前公 ...

  5. 项目源码--Android迷幻岛屿综合游戏

    下载源码 技术要点: 1.游戏开发综合技术 2.多线程机制实现游戏逻辑 3.自定义控件,系统控件等综合图层的使用 4.图层素材动画的综合技术 5.游戏算法的实现 6. OpenGL ES的综合使用 7 ...

  6. spring mvc 接收页面表单List

    很少写博客,如果写的不好请多多包涵! 最近在用Spring mvc时遇到一个问题,在网上搜了很多资料.几乎没看到解决办法! 例如:当我们在做批量添加或者更新时,在Controller层接收表单数据的问 ...

  7. NSURLConnection 网络超时的那些事(转别人整理的)

    NSURLConnection 网络超时的那些事(转别人整理的) 在ios平台上做网络开发最常用的两个类: NSMutableURLRequest *urlRequest = [[NSMutableU ...

  8. Gradle实战:不同编译类型的包同设备共存

    查看原文:http://blog.csdn.net/u010818425/article/details/52335844 Gradle实战系列文章: <Gradle基本知识点与常用配置> ...

  9. (Android学习系列)一,用按钮实现时间的显示

    我们先用AndroidStudio新建一个项目,选择空白模板,然后像其中拖入两个Button,将他们的id分别命名为btDate(显示日期),btTime(显示时间),他的模板XML代码很简单 < ...

  10. Kali linux 2016无法打开virtualbox问题解决

    Kali Linux在安装完virtualbox后,打开虚拟机会出现:kernel driver not installed (rc=1908)错误提示,根据提示,大概可以看出是由于缺少内核模块引起的 ...