一直搞不清楚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. UVA 12904 Load Balancing 暴力

    Load Balancing Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/contest/vi ...

  2. Android实现Filterable通过输入文本框实现联系人自动筛选

    相信大家一定在见过手机通讯录的一个情景就是使用在选人的时候输入文本框里的数据就能自动筛选.实现的效果如下图. 其实实现这样的效果相信大家一定对另外一个控件不陌生那就AutoCompleteTextvi ...

  3. 位图字体生成工具Bitmap Font Generator的使用

    首先,说下为什么要使用这款工具作为cocos2d-x的字体生成工具.其实cocos2d-x能使用的字体生成工具也有好几个,当然了本人也没有全部使用过,就不一一说明了.Bitmap Font Gener ...

  4. ext2元数据结构

    概述           本篇博客主要描述ext2文件系统中的各种典型元数据结构,其中包括文件系统级别的元数据,如超级块,块组描述符等,也包括文件级的元数据,如文件目录项,文件inode等.   ex ...

  5. memmove和memcpy

    1.memmove 函数原型:void *memmove(void *dest, const void *source, size_t count) 返回值说明:返回指向dest的void *指针 参 ...

  6. Golang学习 - io/ioutil 包

    ------------------------------------------------------------ // Discard 是一个 io.Writer 接口,调用它的 Write ...

  7. 优化 App 的启动速度

    App 的启动速度不仅影响我们调试,也直接关系到用户体验.之前有些很久没有打开过的项目,需要花费很长的时间才完成编译:对应的 App 在点击后,许久才出现启动画面.你是否为这些问题苦恼过呢? 这是我观 ...

  8. CSU 1552: Friends 图论匹配+超级大素数判定

    1552: Friends Time Limit: 3 Sec  Memory Limit: 256 MBSubmit: 163  Solved: 34[Submit][Status][Web Boa ...

  9. myecplise 打开报错 Errors occurred during the build. Errors running builder 'DeploymentBuilder' on project 'myf'. Java.lang.NullPointerException

    Errors occurred during the build. Errors running builder 'DeploymentBuilder' on project 'myf'.Java.l ...

  10. Android 高级UI设计笔记14:Gallery(画廊控件)之 3D图片浏览

    1. 利用Gallery组件实现 3D图片浏览器的功能,如下: 2. 下面是详细的实现过程如下: (1)这里我是测试性代码,我的图片是自己添加到res/drawable/目录下的,如下: 但是开发中不 ...