分类
最佳红利优惠线交易平台

怎么处理冷信号与热信号

特殊的热信号.png

怎么处理冷信号与热信号

在 ReactiveCocoa 中除了不可变的信号 RACSignal,也有用于桥接非 怎么处理冷信号与热信号 RAC 代码到 . ReactiveCocoa 世界的『可变』信号 RACSubject。 “Mutable” RACSignal — RACSubject RACSubject 到

细说ReactiveCocoa的冷信号与热信号(三):怎么处理冷信号与热信号

第一篇文章中我们介绍了冷信号与热信号的概念,前一篇文章我们也. 原来在RAC的世界中,所有的热信号都属于一个类——RACSubject。接下来我们来看看究竟它为什么这么“神奇”。 在RAC2.5文档的框架概述中,有着这..

01-RAC之RACSignal 和RACSubject

1. 什么是RAC? RAC是一个开源的框架,其学名为函数响应式编程。 2.RACSignal小试 RACSignal是一个信号类,它的主要作用是: 创建一个信号;. //RACSignal: 信号类,当我们有数据产生,创建一个信号! //1.

细说RAC的冷热信号

RACSignal 冷信号和热信号底层实现分析

前言 由于最近在写关于RACSignal. 1.关于冷信号和热信号的概念2.RACSignal热信号3.RACSignal冷信号4.冷信号是如何转换成热信号的 一. 关于冷信号和热信号的概念 冷热信号的概念是源自于源于.NET框架Reactive Extens

RAC热信号

首先来看如下代码发布信号: RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) < [subscriber sendNext:@1]; [[RACScheduler mainThreadScheduler] afterDelay:1

细说 ReactiveCocoa 的冷信号与热信号(三):怎么处理冷信号与热信号

第一篇文章中我们介绍了冷信号与热信号的概念,前一篇文章我们也讨论了为什么要区分冷信号与热信号,下面我会先为大家揭晓热信号的本质,再给出冷信号转换成热信号的方法。 揭示热信号的本质 在

ReactiveCocoa的冷信号与热信号 怎么处理冷信号与热信号 探讨

背景 ReactiveCocoa(简称RAC)是最初由GitHub团队开发的一套基于Cocoa的FRP框架。FRP即Functional Reactive Programming(函数式响应式编程),其优点是用随时间改变的函数表示用户输入,这样就不需要可变状态了。.

热信号是主动的,即使你没有订阅事件,它仍然会时刻推送 而冷信号是被动的,只有当你订阅的时候,它才会发送消息 热信号可以有多个订阅者,是一对多,信号可以与订阅者共享信息 而冷信号只能一对一,当有不同的订阅.

RACSubject(一)

RACSubject作为RACSignal的子类,也遵循了RACSubscriber协议,而且如果对信号有所了解,应该知道RACSubject就是热信号。接下来就分析下RACSubject的源码。 首先,看下.h文件。里面有关于这个类的注释: /// A .

RAC冷热信号

Hot Observable是主动的,尽管你并没有订阅事件,但是它会时刻推送 Cold Observable是被动的,只有当你订阅的时候,它才会发布消息。. 在 ReactiveCocoa 中,我们使用 RACSignal 来表示冷信号,也

RAC学习一 RACSignal、RACSubject、RACSubscriber、RACDisposable

概念 RAC全称-ReactiveCocoa,是GitHub上的一个开源框架,普遍称之为“函数的响应式编程”,想详细了解什么为什么叫做函数式响应编程,看 袁峥. ①通过RACSignal一个信号,但创建出来的是冷信号 ②通过订阅者订阅.

rac初识之冷热信号

racsignal的信号有冷热之分,. 而热信号则不依赖与订阅者,当它需要发消息的时候,不论有没有订阅者,都会发送。 冷信号如下 RACSignal *signal = [RACSignalcreateSignal:^RACDisposable *(idRACSubscriber> su

开发者头条

冷热信号的概念源于C#的MVVM框架Reactive Extensions中的Hot Observables和Cold Observables:

  1. Hot Observables是主动的,尽管你并没有订阅事件,但是它会时刻推送,就像鼠标移动;而Cold Observables是被动的,只有当你订阅的时候,它才会发布消息。

  2. Hot Observables可以有多个订阅者,是一对多,集合可以与订阅者共享信息;而Cold Observables只能一对一,当有不同的订阅者,消息是重新完整发送。


我们可以看到,信号在18:33:21.681时被创建,18:33:21.793依次接到1、2、3三个值,而在18:33:怎么处理冷信号与热信号 22.683再依次接到1、2、3三个值。说明了变量名为 signal 的这个信号,在两个不同时间段的订阅过程中,分别完整的发送了所有的消息。

首先告诉大家-[RACSignal publish]、- [RACMulticastConnection connect]、- [RACMulticastConnection signal]这几个操作生成了一个热信号。
我们再来关注下输出结果的一些细节:

为什么要区分冷信号与热信号

最前面提到了RAC是一套基于Cocoa的FRP框架,那就来说说FRP,FRP全写是Functional Reactive Programming,中文译作函数响应式编程,是RP(Reactive Programm,响应式编程)的FP(Functional Programming,函数式编程)实现。说起来很拗口。太多的细节不多讨论,我们先关注下它是FP的情况。

这里有一个很重要的概念,就是任何的信号转换即是对原有的信号进行订阅从而产生新的信号。我们可以写出flattenMap的伪代码如下:


除了没有高度复用和缺少一些disposable的处理以外,上述代码可以大致的给我们 flattenMap 的直观处理,我们可以看到其实是在调用这个方法的时候,生成了一个新的信号,在这个新的信号的执行过程中对 self 进行的了 订阅 。我们还需要注意一个细节,就是这个返回信号在未来订阅的时候,才会间接的订阅了 self 。后续的 startWith 、 catchTo 等都可以这样理解。

回到我们的问题,那就是说,在fetchData被flattenMap之后,它就会因为名为title和desc信号的订阅而订阅。而后续我们对desc也进行了flattenMap得到了renderedDesc,那也说明了未来renderedDesc被订阅的时候,fetchData也会被间接订阅。所以我们解释了在后续我们用RAC宏进行绑定的时候,引发的3次fetchData的订阅。由于fetchData是冷信号,所以3次订阅意味着它的过程被执行了3次,也就是网络的3次请求。

接下来也许你会问,如果我的整个计算过程中都没有副作用,是否就不会有这个问题,答案是肯定的,试想下刚才那段代码如果没有网络请求,换成一些标准化的计算会怎样。可以肯定的是我们不会出现bug,但是不要忽视的就是其中的运算我们执行了多次。刚才在介绍纯函数的时候,还有一个概念就是引用透明,我们可以在纯函数式语言(例如Haskell)上进行一定的优化,也就是说纯函数的调用在相同参数下的返回值第二次不需要计算,所以在纯函数式语言里面的FRP并没有冷信号的担忧。然而Objective-C语言中并未对纯函数进行优化。所以拥有大规模运算的冷信号对性能也是有一定影响的。

正确理解冷信号与热信号


上述代码的 怎么处理冷信号与热信号 声明 了一个信号 signal , signal 指明了发送 “1” 这个值后发送 结束 事件。另外 声明 了一个信号 mappedSignal , mappedSignal 指明 signal 的值都进行一个字符串的 转换 。如果仅仅写到这里, sendNext: 和 map: 后面的block其实都没有被执行。

那究竟是何时这些block会执行呢?没错,那就是在订阅之后。订阅mappedSignal之后,还会连带的把signal订阅了。因而预先声明的部分就有了动作。

揭示热信号的本质

A subject, represented by the RACSubject class, is a signal that can be manually controlled.

Subjects can be thought of as the “mutable” 怎么处理冷信号与热信号 variant of a signal, much like NSMutableArray is for NSArray. They are extremely useful for bridging non-RAC code into the world of signals.

For example, instead of handling application logic in block callbacks, the 怎么处理冷信号与热信号 blocks can simply send events to a 怎么处理冷信号与热信号 shared subject instead. The subject can then 怎么处理冷信号与热信号 be returned as a RACSignal, hiding the 怎么处理冷信号与热信号 implementation detail of the callbacks.

Some subjects offer additional behaviors as well. In particular, RACReplaySubject can be used to buffer events for future subscribers, like when a network request finishes before anything is ready to handle the result.

用于多播的 RACMulticastConnection

Note 怎么处理冷信号与热信号 that you shouldn’t create RACMulticastConnection manually. Instead use -publish or -multicast:.

我们不应该直接使用 -initWithSourceSignal:subject: 来初始化一个对象,我们应该通过 RACSignal 的实例方法初始化 RACMulticastConnection 实例。

这两个方法 -publish 和 -multicast: 都是对初始化方法的封装,并且都会返回一个 RACMulticastConnection 对象,传入的 sourceSignal 就是当前信号, subject 就是用于对外广播的 RACSubject 对象。

RACSignal 和 RACMulticastConnection

网络请求在客户端其实是一个非常昂贵的操作,也算是多级缓存中最慢的一级,在使用 ReactiveCocoa 处理业务需求中经常会遇到下面的情况:

通过订阅发出网络请求的信号经常会被多次订阅,以满足不同 怎么处理冷信号与热信号 UI 组件更新的需求,但是以上代码却有非常严重的问题。

每一次在 RACSignal 上执行 -subscribeNext: 以及类似方法时,都会发起一次新的网络请求,我们希望避免这种情况的发生。

为了解决上述问题,我们使用了 -publish 方法获得一个多播对象 RACMulticastConnection ,更改后的代码如下:

在这个例子中,我们使用 -publish 方法生成实例,订阅者不再订阅源信号,而是订阅 RACMulticastConnection 怎么处理冷信号与热信号 怎么处理冷信号与热信号 中的 RACSubject 热信号,最后通过 -connect 方法触发源信号中的任务。

publish 和 multicast 方法

我们再来看一下 -publish 和 -multicast: 这两个方法的实现:

当 -publish 方法调用时相当于向 -multicast: 传入了 RACSubject 。

-publish 只是对 -multicast: 方法的简单封装,它们都是通过 RACMulticastConnection 私有的初始化方法 -initWithSourceSignal:subject: 创建一个新的实例。

在使用 -multicast: 方法时,传入的信号其实就是用于广播的信号;这个信号必须是一个 RACSubject 本身或者它的子类:

传入 -multicast: 方法的一般都是 RACSubject 或者 RACReplaySubject 对象。

订阅源信号的时间点

订阅 connection.signal 中的数据流时,其实只是向多播对象中的热信号 RACSubject 持有的数组中加入订阅者,而这时刚刚创建的 RACSubject 中并没有任何的消息。

只有在调用 -connect 方法之后, RACSubject 才会订阅源信号 sourceSignal 。

这时源信号的 didSubscribe 代码块才会执行,向 RACSubject 推送消息,消息向下继续传递到 RACSubject 所有的订阅者中。

-connect 方法通过 -subscribe: 实际上建立了 RACSignal 和 RACSubject 之间的连接,这种方式保证了 RACSignal 中的 didSubscribe 代码块只执行了一次。

所有的订阅者不再订阅原信号,而是订阅 RACMulticastConnection 持有的热信号 RACSubject ,实现对冷信号的一对多传播。

在 RACMulticastConnection 中还有另一个用于连接 RACSignal 和 RACSubject 信号的 -autoconnect 怎么处理冷信号与热信号 方法:

它保证了在 -autoconnect 方法返回的对象被第一次订阅时,就会建立源信号与热信号之间的连接。

使用 RACReplaySubject 订阅源信号

虽然使用 -publish 方法已经能够解决大部分问题了,但是在 -connect 方法调用之后才订阅的订阅者并不能收到消息。

如何才能保存 didSubscribe 执行过程中发送的消息,并在 -connect 调用之后也可以收到消息?这时,我们就要使用 -multicast: 方法和 RACReplaySubject 来完成这个需求了。

除了使用上述的代码,也有一个更简单的方式创建包含 RACReplaySubject 对象的 RACMulticastConnection :

-replay 方法和 -publish 差不多,只是内部封装的热信号不同,并在方法调用时就连接原信号:

除了 -replay 方法, RACSignal 中还定义了与 RACMulticastConnection 中相关的其它 -replay 方法:

三个方法都会在 RACMulticastConnection 初始化时传入一个 RACReplaySubject 对象,不过却有一点细微的差别:

相比于 -replay 方法, -replayLast 方法生成的 RACMulticastConnection 中热信号的容量为 1 :

而 replayLazily 会在返回的信号被第一次订阅时,才会执行 -connect 方法:

RACMulticastConnection 在处理冷热信号相互转换时非常好用,在 RACSignal 中也提供了很多将原有的冷信号通过 RACMulticastConnection 转换成热信号的方法。

References

Go 语言设计与实现

各位读者朋友,很高兴大家通过本博客学习 Go 语言,感谢一路相伴! 《Go语言设计与实现》 的纸质版图书已经上架京东,本书目前已经四印,印数超过 10,000 册,有需要的朋友请点击 链接 或者下面的图片购买。

ReactiveCocoa4中的冷信号和热信号

怎么处理冷信号与热信号
热信号.png

2. 冷信号 SignalProducer

同样为了验证 SignalProducer 具有冷信号的特点,进行如下实验

  1. 0s创建冷信号 producer
  2. 1s producer 发送第一个包
  3. 2s producer 发送第二个包
  4. 2s,订阅冷信号 producer 该订阅者命名为订阅者1
  5. 3s,订阅冷信号 producer 该订阅者命名为订阅者2
  1. 45.683s,创建冷信号
  2. 47.886s,即2s后,订阅者1订阅冷信号 producer
  3. 48.685s,即3s后,订阅者2订阅冷信号 producer
  4. 48.889s,即3s后, producer 发送第一个包,(为什么是在3s后发送?)
  5. 48.892s,即3s后,与此同时,订阅者1接收到 producer 发出的第一个包
  6. 49.685s,即4s后, producer 再次发送第一个包(为什么又发送一次?)
  7. 49.686s,即4s后,与此同时,订阅者2接收到 producer 发送的第一个包
  8. 49.890s,即4s后, producer 发送第二个包
  9. 49.890s,即4s后,与此同时,订阅者1接收到 怎么处理冷信号与热信号 producer 发出的第二个包
  10. 50.686s,即5s后, producer 再次发送第二个包
  11. 50.686s,即5s后,与此同时,订阅者2接收到 producer 发出的第二个包

为什么 producer是在 3s后发送第一个包?

因为,订阅者1是在2s后才订阅冷信号 producer ,然后 producer 在1s后发给订阅者1第一个包(注意:是发给订阅者1),这也解释了为什么 producer 每个包会发两遍

producer 再次发送第一个包是发送给订阅者2的,而订阅者2是在3s后才订阅冷信号 producer ,然后 producer 在1s后发给订阅者2第一个包

上面分析也证明了 SignalProducer 具有冷信号的特点

  1. SignalProducer`是一对一发送,这句话可能不好理解。这里可以理解成,有几个订阅者,冷信号就发送几次同样的信号
  2. 每个订阅者都能接收到同样的事件。例如上面订阅者2在3s后订阅,那它就在4s后和5s后接收到事件


冷信号.png

由上述分析,可以得知RAC2和RAC4中的冷热信号有如下关系:

RAC2 RAC4
冷信号 RACSignal SignalProducer
热信号 RACSubject Singal

3. 冷信号的副作用(Side Effect)

在细说ReactiveCocoa的冷信号与热信号(二):为什么要区分冷热信号提出了,如果冷信号中包含网络请求,那么每次订阅这个冷信号都会发送网络请求,而且任何的信号转换即是对原有的信号进行订阅从而产生新的信号

4. 特殊的热信号

这里假设 signalFromNetwork() 是发送网络请求后获得的一个热信号 signal (注意是热信号)怎么处理冷信号与热信号 ,然后订阅该信号,这里简单地打印事件。

但是,如果运行这段代码,并没有输出任何结果。是因为返回的信号没有发送任何 next 事件吗?

实际上,返回的热信号发送了一个 next 事件,但是订阅者没有收到。

这是我们可以看到有了输出结果。这就说明了之前订阅者为什么没有接收到事件,因为在订阅者订阅热信号之前,热信号就已经发送了事件。而这次是因为热信号延迟了1s才发送事件,所以订阅者才能接收到数据

这就是RAC2中的 RACReplaySubject ,这种信号特点在于:

  1. 无论是否有订阅者订阅该信号,该信号都会发送事件,这点和热信号一致
  2. 无论订阅者何时订阅信号,订阅者都能立即接收到该信号所发送的事件,这点和冷信号相似,但有很大的不同

而在RAC4中,我们使用 SignalProducer.buffer(Int) 这个方法来代替 RACReplaySubject Using SignalProducer.buffer instead of replaying

同样通过一个实验来证明 SignalProducer.buffer 和 RACReplaySubject 具有同样的特点

  1. 创建特殊的热信号,并在1s,4s后发送两个包
  2. 在0s和1.5s时,订阅者1、2订阅了该信号
  1. 所有事件,信号只发送了一次
  2. 订阅者1是0s订阅的,毫无疑问,订阅者1可以接收到所有事件
  3. 订阅者2是4s才订阅的,而此时信号已经发出了所有的事件,如果是普通的热信号,订阅者2是接受不到任何事件的,但这里订阅者2却同时接收到了信号发送的所有事件,就像所有的事件缓存起来一样

根据特点,我们可以得到 ReplaySubject 和 SingalProducer.buffer 产生的信号的图

特殊的热信号.png

现在回到冷信号副作用的问题上,因为 buffer 返回的信号,具有热信号的特点,不会产生副作用。同时又能像冷信号一样,确保所有的订阅者都能接收到事件。

现在将本节 signalFromNetwork() 作出一些更改

然而,有些情况下,类似 signalFromNetwork() 这种方法是别人提供的,而且返回的就是一个冷信号 SignalProducer 这种情况,你可能无法修改 signalFromNetwork() 内部代码。那要如何处理这个冷信号,避免副作用呢?

在RAC4.0,SignalProducer添加了 replayLazily 这个方法,避免了冷信号的副作用 Added SignalProducer.replayLazily for multicasting

我们将 signalFromNetwork() 改成返回冷信号

只是在返回信号调用 replayLazily 方法,其余都不变

貌似和之前和输出结果有点不一样,忽略那个 start 吧!只有 怎么处理冷信号与热信号 Subscriber1 和 signal send hello 顺序颠倒了,从时间上来看,信号发送事件的时间延迟了。

5. buffer 和 replayLazily 中的参数 capacity

在使用这两个方法时,需要传一个名为 capacity 参数,那这个参数是什么意思呢?感兴趣的同学可以先去看看官方文档是怎么解释的。

很简单的一段代码,信号发送三个 next 怎么处理冷信号与热信号 事件,2s后,订阅者订阅该信号。

如果你以为输出的是A B C,那就请看实际运行结果

接下来,我们把 buffer(1) 改成 buffer(怎么处理冷信号与热信号 3)

到这就应该明白 capacity 的含义,就是指 SignalProducer 为订阅者缓存多少个事件,如果发送事件数超过缓存容量,则先发送的事件会被后发送的事件覆盖,这也解释了为什么当 capacity=1 时,只输出C