Understanding Hot and Cold Signals in ReactiveCocoa (RAC)
The article explains that in ReactiveCocoa cold signals are plain RACSignal instances that start their work per subscriber, while hot signals are RACSubject (or subclasses) that broadcast a single execution to all observers, and demonstrates converting cold to hot using subjects, multicast operators, and replay helpers such as replayLazily to share network requests.
ReactiveCocoa (RAC) is an FRP framework originally developed by the GitHub team. This article clarifies the concepts of cold and hot signals in RAC and shows how to convert a cold signal into a hot one.
Hot Signal Essence
In RAC there is no dedicated "hot signal" class; all hot signals are instances of RACSubject (or its subclasses). A subject is mutable, acts as a bridge between non‑RAC code and RAC signals, and can have additional behavior such as buffering events with RACReplaySubject.
Simple Experiment
RACSubject *subject = [RACSubject subject];
RACSubject *replaySubject = [RACReplaySubject subject];
[[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{
// Subscriber 1
[subject subscribeNext:^(id x) {
NSLog(@"Subscriber 1 get a next value: %@ from subject", x);
}];
[replaySubject subscribeNext:^(id x) {
NSLog(@"Subscriber 1 get a next value: %@ from replay subject", x);
}];
// Subscriber 2
[subject subscribeNext:^(id x) {
NSLog(@"Subscriber 2 get a next value: %@ from subject", x);
}];
[replaySubject subscribeNext:^(id x) {
NSLog(@"Subscriber 2 get a next value: %@ from replay subject", x);
}];
}];
[[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{
[subject sendNext:@"send package 1"];
[replaySubject sendNext:@"send package 1"];
}];
[[RACScheduler mainThreadScheduler] afterDelay:1.1 schedule:^{
// Subscriber 3
[subject subscribeNext:^(id x) {
NSLog(@"Subscriber 3 get a next value: %@ from subject", x);
}];
[replaySubject subscribeNext:^(id x) {
NSLog(@"Subscriber 3 get a next value: %@ from replay subject", x);
}];
// Subscriber 4
[subject subscribeNext:^(id x) {
NSLog(@"Subscriber 4 get a next value: %@ from subject", x);
}];
[replaySubject subscribeNext:^(id x) {
NSLog(@"Subscriber 4 get a next value: %@ from replay subject", x);
}];
}];
[[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
[subject sendNext:@"send package 2"];
[replaySubject sendNext:@"send package 2"];
}];The timeline shows that all four subscribers share the same subject; when the subject emits a value, every current subscriber receives it. Subscribers that subscribe later (3 and 4) miss the first emission, which is the typical behavior of a hot signal.
Cold vs. Hot
By contrast, a plain RACSignal (excluding RACSubject) behaves as a cold signal: each subscription triggers the original block independently, starting from the beginning.
Converting a Cold Signal to a Hot Signal (Broadcast)
The usual way is to subscribe the cold signal to a RACSubject and let other observers subscribe to that subject. Example:
RACSignal *coldSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"Cold signal be subscribed.");
[[RACScheduler mainThreadScheduler] afterDelay:1.5 schedule:^{
[subscriber sendNext:@"A"];
}];
[[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
[subscriber sendNext:@"B"];
}];
[[RACScheduler mainThreadScheduler] afterDelay:5 schedule:^{
[subscriber sendCompleted];
}];
return nil;
}];
RACSubject *subject = [RACSubject subject];
[[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{
[coldSignal subscribe:subject];
}];
[subject subscribeNext:^(id x) {
NSLog(@"Subscriber 1 receive value:%@.", x);
}];
[[RACScheduler mainThreadScheduler] afterDelay:4 schedule:^{
[subject subscribeNext:^(id x) {
NSLog(@"Subscriber 2 receive value:%@.", x);
}];
}];This pattern ensures the underlying cold signal is executed only once, while the subject distributes the events to all subscribers, making it a hot signal.
RAC Multicast Helpers
RAC provides convenience methods that internally use multicast: with a subject:
- (RACMulticastConnection *)publish;
- (RACMulticastConnection *)multicast:(RACSubject *)subject;
- (RACSignal *)replay;
- (RACSignal *)replayLast;
- (RACSignal *)replayLazily;The most important is - (RACMulticastConnection *)multicast:(RACSubject *)subject. It creates a RACMulticastConnection that holds the source signal and the subject. Calling connect subscribes the source signal to the subject exactly once; subsequent subscriptions to connection.signal receive the shared events.
Other helpers are thin wrappers: publish creates a plain RACSubject and returns its connection. replay uses a RACReplaySubject and connects immediately, so later subscribers get the whole history. replayLast buffers only the most recent value. replayLazily defers the connection until the first subscription.
Practical Usage
For network requests you can avoid multiple fetches by applying replayLazily to the cold signal:
RACSignal *fetchData = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// network request …
} ] replayLazily];Subsequent transformations (e.g., extracting title, desc) will share the same underlying request, reducing redundant work.
In summary, RACSubject and its subclasses are hot signals, while RACSignal (excluding subjects) are cold signals. Using the multicast family of operators or manually broadcasting via a subject provides a clean way to turn cold signals into hot ones.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Meituan Technology Team
Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
