ReactiveCocoa比较区分replay, replayLast和replayLazily
一直搞不清楚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的更多相关文章
- 转:RAC中比较replay, replayLast, and replayLazily
A co-worker recently asked me about the difference between -replay, -replayLast, and -replayLazily i ...
- 一次MVVM+ReactiveCocoa实践
前言 学习MVVM和ReactiveCocoa(简称RAC)也有一段时间了,不过都仅限于看博客,一直对这两个东西很感兴趣,觉得很创新,也一直想找个机会在项目中实践一下,但是还是有一些顾虑,毕竟没有实践 ...
- ReactiveCocoa v2.5 源码解析 之 架构总览
ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,它受 Functional Reactive Programming 的启发,是 Justin Spahr-Summers 和 J ...
- ReactiveCocoa的冷信号与热信号 探讨
背景 ReactiveCocoa(简称RAC)是最初由GitHub团队开发的一套基于Cocoa的FRP框架.FRP即Functional Reactive Programming(函数式响应式编程), ...
- RACSignal的Subscription深入
ReactiveCocoa是一个FRP的思想在Objective-C中的实现框架,目前在美团的项目中被广泛使用.对于ReactiveCocoa的基本用法,网上有很多相关的资料,本文不再讨论.RACSi ...
- ReactiveCocoa_v2.5 源码解析之架构总览
ReactiveCocoa 是一个 iOS 中的函数式响应式编程框架,它受 Functional Reactive Programming 的启发,是 Justin Spahr-Summers 和 J ...
- 走进ReactiveCocoa的世界
在学习ReactiveCocoa之前,先学习一下概念 ReactiveCocoa 是一套开源的基于Cocoa的FRP框架 .FRP的全称是Functional Reactive Programming ...
- ReactiveCocoa基础知识内容
本文记录一些关于学习ReactiveCocoa基础知识内容,对于ReactiveCocoa相关的概念如果不了解可以网上搜索:RACSignal有很多方法可以来订阅不同的事件类型,ReactiveCoc ...
- ReactiveCocoa应用篇(二)
上一篇介绍了ReactiveCocoa的常用类,已经基本满足项目中的简单应用要求,但是针对复杂的功能还需要其它的类来协同处理.ReactiveCocoa提供了强大的流程处理功能来解决复杂的问题,包括事 ...
随机推荐
- uoj #5. 【NOI2014】动物园 kmp
#5. [NOI2014]动物园 Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://uoj.ac/problem/5 Description 近日 ...
- cocos2dx A*算法
头文件和源文件拷贝到项目中就能用了! have fun 使用cocos2dx 3.2 原理都一样 淡蓝色的点是地图 深蓝色的点是障碍物 绿色的点是路径 暗绿色的点是搜寻过的点 红色的点是按路径行走的点 ...
- Linux - wc统计文件行数、单词数或字节数
一 wc简单介绍 wc命令用来打印文件的文本行数.单词数.字节数等(print the number of newlines, words, and bytes in files).在Windows的 ...
- Swift2.0 中的String(二):基本操作
Swift中的字符串,第二篇,基本操作.其他的几篇传送门(GitHub打不开链接的同学请自行把地址github改成gitcafe,或者直接去归档里找:-P): Swift2.0 中的String(一) ...
- SVM多分类
http://www.matlabsky.com/thread-9471-1-1.htmlSVM算法最初是为二值分类问题设计的,当处理多类问题时,就需要构造合适的多类分类器.目前,构造SVM多类分类器 ...
- 分享10款常用的jQuery焦点图插件
爱编程一直在收集整理编程相关的知识和解决方案,今天小编为大家带来10款非常常用的jquery焦点图插件. 1.jQuery可自动播放动画的焦点图插件 之前我们已经分享过很多非常实用的jQuery焦点图 ...
- 21 Free SEO Tools For Bloggers--reference
http://dizyne.net/21-free-seo-tools-for-bloggers/ What do you think is important in a website? Yes, ...
- 自定义强大的C#网络操作基础类(NetHelper)
using System; using System.Text;using System.Net.Sockets;using System.Net.Mail;using System.Net; nam ...
- 经典的iptables shell脚本
PS:这个iptables脚本不错,很实用,根据实际应用改一下就可以自己用.分享出来,供大家来参考.原作者佚名.源代码如下: #!/bin/sh modprobe ipt_MASQUERADE mod ...
- 【Shell脚本学习18】Shell for循环
与其他编程语言类似,Shell支持for循环. for循环一般格式为: for 变量 in 列表 do command1 command2 ... commandN done 列表是一组值(数字.字符 ...