How to make RACSignal to become hot?

2019-02-13 21:08发布

问题:

ReactiveCocoa can convert the signal to "hot" signal by calling its -subscribeCompleted:. But I think this method is quite verbose if you do not care about the result (i.e. no subscribers).

RACDisposable *animationDisposable = [[self play:animation] subscribeCompleted:^{
    // just to make the animation play
}];

And these 3 lines are not expressive enough to show my intention.

Is there any method for similar purpose? Thanks!

回答1:

I want to do nothing except making it hot (=make it run once).

"You keep using that word. I do not think it means what you think it means."

A "hot signal" is a signal that sends values (and presumably does work) regardless of whether it has any subscribers. A "cold signal" is a signal that defers its work and the sending of any values until it has a subscriber. And a cold signal will perform its work and send values for each subscriber.

If you want to make a cold signal run only once but have multiple subscribers, you need to multicast the signal. Multicasting is a pretty simple concept, that works like this:

  1. Create a RACSubject to proxy the values sent by the signal you want to execute once.
  2. Subscribe to the subject as many times as needed.
  3. Create a single subscription to the signal you want to execute only once, and for every value sent by the signal, send it to the subject with [subject sendNext:value].

However, you can and should use RACMulticastConnection to do all of the above with less code:

RACMulticastConnection *connection = [signal publish];
[connection.signal subscribe:subscriberA];
[connection.signal subscribe:subscriberB];
[connection.signal subscribe:subscriberC];
[connection connect]; // This will cause the original signal to execute once.
                      // But each of subscriberA, subscriberB, and subscriberC
                      // will be sent the values from `signal`.


回答2:

If you do not care about the output of the signal (and for some reason you really want play to be a signal), you may want to make a command. A command causes a signal to be executed via some sort of event (such as a ui button press or other event). Simply create the Signal, add it to a command, then when you need to run it, execute it.

@weakify(self);
RACCommand * command = [[RACCommand alloc] initWithSignalBlock:^(id input) {
  @strongify(self);
  return [self play:animation];
}];

//This causes the signal to be ran
[command execute:nil];

//Or you could assign the command to a button so it is executed 
// when the button is pressed
playButton.rac_command = command;