RAC Basic Operators_part1
2015-07-29 13:10
323 查看
This document explains some of the most common operators used in ReactiveCocoa, and includes examples demonstrating their use.
Note that “operators,” in this context, refers to functions that transform signals and signal
producers, not custom Swift operators. In other words, these are composable primitives provided by ReactiveCocoa for working with event streams.
This document will use the term “event stream” when dealing with concepts that apply to both
When the distinction matters, the types will be referred to by name.
Performing
side effects with event streams
Observation
Injecting effects
Operator
composition
Lifting
Pipe
Transforming
event streams
Mapping
Filtering
Aggregating
Combining
event streams
Combining
latest values
Zipping
Flattening
producers
Concatenating
Merging
Switching
to the latest
Handling errors
Catching errors
Mapping errors
Retrying
It takes an
Alternatively, callbacks for the
can be provided which will be called when a corresponding event occurs.
Note that it is not necessary to provide all four parameters - all of them are optional, you only need to provide callbacks for the events you care about.
Side effects can be injected on a
without actually subscribing to it.
Similar to
about.
Note that nothing will be printed until
The
other using the
using the
This will create a new
just as if the operator had been applied to each produced
The
so it can be used to apply them directly to
These operators transform an event stream into a new stream.
The
Interactive visualisation of the
The
Interactive visualisation of the
The
value is only sent after the input stream completes.
The
value is only sent after the input stream completes.
Interactive visualisation of the
These operators combine values from multiple event streams into a new, unified stream.
The
The resulting stream will only send its first value after each input has sent at least one value. After that, new values on any of the inputs will result in a new value on the output.
The
Interactive visualisation of the
The
to the Nth elements of the input streams.
That means the Nth value of the output stream cannot be sent until each input has sent at least N values.
The
Interactive visualisation of the
The
into a single
To understand, why there are different strategies and how they compare to each other, take a look at this example and imagine the column offsets as time:
Note, how the values interleave and which values are even included in the resulting array.
The
to the outer
the flattened producer and terminates it.
Interactive visualisation of the
The
The outer producer is started immediately. Each subsequent producer is not started until the preceeding one has completed. Errors are immediately forwarded to the flattened producer.
Interactive visualisation of the
The
These operators are used to handle errors that might occur on an event stream.
The
then starts a new
The
error up to
If the
the resulting
used in the example above instead of
The
The
The given stream will still not actually generate errors, but this is useful because some operators to combine
streamsrequire the inputs to have matching error types.
This document explains some of the most common operators used in ReactiveCocoa, and includes examples demonstrating their use.
Operators that apply to sequences and signals are
known as stream operators.
Performing
side effects with signals
Subscription
Injecting
effects
Transforming
streams
Mapping
Filtering
Combining
streams
Concatenating
Flattening
Mapping
and flattening
Combining
signals
Sequencing
Merging
Combining
latest values
Switching
Most signals start out "cold," which means that they will not do any work until subscription.
Upon subscription, a signal or its subscribers can
perform side effects, like logging to the console, making a network request, updating the user interface, etc.
Side effects can also be injected into
a signal, where they won't be performed immediately, but will instead take effect with each subscription later.
The -subscribe… methods give you
access to the current and future values in a signal:
For a cold signal, side effects will be performed once per subscription:
This behavior can be changed using a connection.
The -do… methods add
side effects to a signal without actually subscribing to it:
These operators transform a single stream into a new stream.
The -map: method is used to transform
the values in a stream, and create a new stream with the results:
The -filter: method uses a block
to test each value, including it into the resulting stream only if the test passes:
These operators combine multiple streams into a single new stream.
The -concat: method appends one
stream's values to another:
The -flatten operator is applied
to a stream-of-streams, and combines their values into a single new stream.
Sequences are concatenated:
Signals are merged:
Flattening isn't that
interesting on its own, but understanding how it works is important for -flattenMap:.
the streams returned will be flattened down into a single stream. In other words, it's -map: followed
by -flatten.
This can be used to extend or edit sequences:
Or create multiple signals of work which are automatically recombined:
These operators combine multiple signals into a single new RACSignal.
-then: starts the original
signal, waits for it to complete, and then only forwards the values from a new signal:
This is most useful for executing all the side effects of one signal, then starting another, and only returning the second signal's values.
The +merge: method will
forward the values from many signals into a single stream, as soon as those values arrive:
The +combineLatest: and
will watch multiple signals for changes, and then send the latest values from all of them when a change occurs:
Note that the combined signal will only send its first value when all of the inputs have sent at least one. In the example above,
never forwarded because
The -switchToLatest operator
is applied to a signal-of-signals, and always forwards the values from the latest signal:
Note that “operators,” in this context, refers to functions that transform signals and signal
producers, not custom Swift operators. In other words, these are composable primitives provided by ReactiveCocoa for working with event streams.
This document will use the term “event stream” when dealing with concepts that apply to both
Signaland
SignalProducer.
When the distinction matters, the types will be referred to by name.
Performing
side effects with event streams
Observation
Injecting effects
Operator
composition
Lifting
Pipe
Transforming
event streams
Mapping
Filtering
Aggregating
Combining
event streams
Combining
latest values
Zipping
Flattening
producers
Concatenating
Merging
Switching
to the latest
Handling errors
Catching errors
Mapping errors
Retrying
Performing
side effects with event streams
Observation
Signals can be observed with the
observefunction.
It takes an
Observeras argument to which any future events are sent.
signal.observe(Signal.Observer { event in switch event { case let .Next(next): println("Next: \(next)") case let .Error(error): println("Error: \(error)") case .Completed: println("Completed") case .Interrupted: println("Interrupted") } })
Alternatively, callbacks for the
Next,
Error,
Completedand
Interruptedevents
can be provided which will be called when a corresponding event occurs.
signal.observe(next: { next in println("Next: \(next)") }, error: { error in println("Error: \(error)") }, completed: { println("Completed") }, interrupted: { println("Interrupted") })
Note that it is not necessary to provide all four parameters - all of them are optional, you only need to provide callbacks for the events you care about.
observeis also available as operator that can be used with |>
Injecting
effects
Side effects can be injected on a SignalProducerwith the
onoperator
without actually subscribing to it.
let producer = signalProducer |> on(started: { println("Started") }, event: { event in println("Event: \(event)") }, error: { error in println("Error: \(error)") }, completed: { println("Completed") }, interrupted: { println("Interrupted") }, terminated: { println("Terminated") }, disposed: { println("Disposed") }, next: { next in println("Next: \(next)") })
Similar to
observe, all the parameters are optional and you only need to provide callbacks for the events you care
about.
Note that nothing will be printed until
produceris started (possibly somewhere else).
Operator
composition
Pipe
The |>operator can be used to apply a primitive to an event stream. Multiple operators can be chained after each
other using the
|>operator:
intSignal |> filter { num in num % 2 == 0 } |> map(toString) |> observe(next: { string in println(string) })
Lifting
Signaloperators can be lifted to operate upon
SignalProducers
using the
liftmethod.
This will create a new
SignalProducerwhich will apply the given operator to every
Signalcreated,
just as if the operator had been applied to each produced
Signalindividually.
The
|>operator implicitly lifts
Signaloperators,
so it can be used to apply them directly to
SignalProducers.
Transforming
event streams
These operators transform an event stream into a new stream.
Mapping
The mapoperator is used to transform the values in a event stream, creating a new stream with the results.
let (signal, sink) = Signal<String, NoError>.pipe() signal |> map { string in string.uppercaseString } |> observe(next: println) sendNext(sink, "a") // Prints A sendNext(sink, "b") // Prints B sendNext(sink, "c") // Prints C
Interactive visualisation of the
mapoperator.
Filtering
The filteroperator is used to only include values in an event stream that satisfy a predicate.
let (signal, sink) = Signal<Int, NoError>.pipe() signal |> filter { number in number % 2 == 0 } |> observe(next: println) sendNext(sink, 1) // Not printed sendNext(sink, 2) // Prints 2 sendNext(sink, 3) // Not printed sendNext(sink, 4) // prints 4
Interactive visualisation of the
filteroperator.
Aggregating
The reduceoperator is used to aggregate a event stream’s values into a single combined value. Note that the final
value is only sent after the input stream completes.
let (signal, sink) = Signal<Int, NoError>.pipe() signal |> reduce(1) { $0 * $1 } |> observe(next: println) sendNext(sink, 1) // nothing printed sendNext(sink, 2) // nothing printed sendNext(sink, 3) // nothing printed sendCompleted(sink) // prints 6
The
collectoperator is used to aggregate a event stream’s values into a single array value. Note that the final
value is only sent after the input stream completes.
let (signal, sink) = Signal<Int, NoError>.pipe() let collected = signal |> collect collected.observe(next: println) sendNext(sink, 1) // nothing printed sendNext(sink, 2) // nothing printed sendNext(sink, 3) // nothing printed sendCompleted(sink) // prints [1, 2, 3]
Interactive visualisation of the
reduceoperator.
Combining
event streams
These operators combine values from multiple event streams into a new, unified stream.
Combining
latest values
The combineLatestfunction combines the latest values of two (or more) event streams.
The resulting stream will only send its first value after each input has sent at least one value. After that, new values on any of the inputs will result in a new value on the output.
let (numbersSignal, numbersSink) = Signal<Int, NoError>.pipe() let (lettersSignal, lettersSink) = Signal<String, NoError>.pipe() combineLatest(numbersSignal, lettersSignal) |> observe(next: println, completed: { println("Completed") }) sendNext(numbersSink, 0) // nothing printed sendNext(numbersSink, 1) // nothing printed sendNext(lettersSink, "A") // prints (1, A) sendNext(numbersSink, 2) // prints (2, A) sendCompleted(numbersSink) // nothing printed sendNext(lettersSink, "B") // prints (2, B) sendNext(lettersSink, "C") // prints (2, C) sendCompleted(lettersSink) // prints "Completed"
The
combineLatestWithoperator works in the same way, but as an operator.
Interactive visualisation of the
combineLatestoperator.
Zipping
The zipfunction joins values of two (or more) event streams pair-wise. The elements of any Nth tuple correspond
to the Nth elements of the input streams.
That means the Nth value of the output stream cannot be sent until each input has sent at least N values.
let (numbersSignal, numbersSink) = Signal<Int, NoError>.pipe() let (lettersSignal, lettersSink) = Signal<String, NoError>.pipe() zip(numbersSignal, lettersSignal) |> observe(next: println, completed: { println("Completed") }) sendNext(numbersSink, 0) // nothing printed sendNext(numbersSink, 1) // nothing printed sendNext(lettersSink, "A") // prints (0, A) sendNext(numbersSink, 2) // nothing printed sendCompleted(numbersSink) // nothing printed sendNext(lettersSink, "B") // prints (1, B) sendNext(lettersSink, "C") // prints (2, C) & "Completed"
The
zipWithoperator works in the same way, but as an operator.
Interactive visualisation of the
zipoperator.
Flattening
producers
The flattenoperator transforms a
SignalProducer-of-
SignalProducers
into a single
SignalProducerwhose values are forwarded from the inner producer in accordance with the provided
FlattenStrategy.
To understand, why there are different strategies and how they compare to each other, take a look at this example and imagine the column offsets as time:
let values = [ // imagine column offset as time [ 1, 2, 3 ], [ 4, 5, 6 ], [ 7, 8 ], ] let merge = [ 1, 4, 2, 7,5, 3,8,6 ] let concat = [ 1, 2, 3,4, 5, 6,7, 8] let latest = [ 1, 4, 7, 8 ]
Note, how the values interleave and which values are even included in the resulting array.
Merging
The .Mergestrategy immediately forwards every value of the inner
SignalProducers
to the outer
SignalProducer. Any error sent on the outer producer or any inner producer is immediately sent on
the flattened producer and terminates it.
let (producerA, lettersSink) = SignalProducer<String, NoError>.buffer(5) let (producerB, numbersSink) = SignalProducer<String, NoError>.buffer(5) let (signal, sink) = SignalProducer<SignalProducer<String, NoError>, NoError>.buffer(5) signal |> flatten(FlattenStrategy.Merge) |> start(next: println) sendNext(sink, producerA) sendNext(sink, producerB) sendCompleted(sink) sendNext(lettersSink, "a") // prints "a" sendNext(numbersSink, "1") // prints "1" sendNext(lettersSink, "b") // prints "b" sendNext(numbersSink, "2") // prints "2" sendNext(lettersSink, "c") // prints "c" sendNext(numbersSink, "3") // prints "3"
Interactive visualisation of the
flatten(.Merge)operator.
Concatenating
The .Concatstrategy is used to serialize work of the inner
SignalProducers.
The outer producer is started immediately. Each subsequent producer is not started until the preceeding one has completed. Errors are immediately forwarded to the flattened producer.
let (producerA, lettersSink) = SignalProducer<String, NoError>.buffer(5) let (producerB, numbersSink) = SignalProducer<String, NoError>.buffer(5) let (signal, sink) = SignalProducer<SignalProducer<String, NoError>, NoError>.buffer(5) signal |> flatten(FlattenStrategy.Concat) |> start(next: println) sendNext(sink, producerA) sendNext(sink, producerB) sendCompleted(sink) sendNext(numbersSink, "1") // nothing printed sendNext(lettersSink, "a") // prints "a" sendNext(lettersSink, "b") // prints "b" sendNext(numbersSink, "2") // nothing printed sendNext(lettersSink, "c") // prints "c" sendCompleted(lettersSink) // prints "1", "2" sendNext(numbersSink, "3") // prints "3" sendCompleted(numbersSink)
Interactive visualisation of the
flatten(.Concat)operator.
Switching
to the latest
The .Lateststrategy forwards only values from the latest input
SignalProducer.
let (producerA, sinkA) = SignalProducer<String, NoError>.buffer(5) let (producerB, sinkB) = SignalProducer<String, NoError>.buffer(5) let (producerC, sinkC) = SignalProducer<String, NoError>.buffer(5) let (signal, sink) = SignalProducer<SignalProducer<String, NoError>, NoError>.buffer(5) signal |> flatten(FlattenStrategy.Latest) |> start(next: println) sendNext(sink, producerA) // nothing printed sendNext(sinkC, "X") // nothing printed sendNext(sinkA, "a") // prints "a" sendNext(sinkB, "1") // nothing printed sendNext(sink, producerB) // prints "1" sendNext(sinkA, "b") // nothing printed sendNext(sinkB, "2") // prints "2" sendNext(sinkC, "Y") // nothing printed sendNext(sinkA, "c") // nothing printed sendNext(sink, producerC) // prints "X", "Y" sendNext(sinkB, "3") // nothing printed sendNext(sinkC, "Z") // prints "Z"
Handling
errors
These operators are used to handle errors that might occur on an event stream.
Catching
errors
The catchoperator catches any error that may occur on the input
SignalProducer,
then starts a new
SignalProducerin its place.
let (producer, sink) = SignalProducer<String, NSError>.buffer(5) let error = NSError(domain: "domain", code: 0, userInfo: nil) producer |> catch { error in SignalProducer<String, NSError>(value: "Default") } |> start(next: println) sendNext(sink, "First") // prints "First" sendNext(sink, "Second") // prints "Second" sendError(sink, error) // prints "Default"
Retrying
The retryoperator will restart the original
SignalProduceron
error up to
counttimes.
var tries = 0 let limit = 2 let error = NSError(domain: "domain", code: 0, userInfo: nil) let producer = SignalProducer<String, NSError> { (sink, _) in if tries++ < limit { sendError(sink, error) } else { sendNext(sink, "Success") sendCompleted(sink) } } producer |> on(error: {e in println("Error")}) // prints "Error" twice |> retry(2) |> start(next: println, // prints "Success" error: { _ in println("Signal Error")})
If the
SignalProducerdoes not succeed after
counttries,
the resulting
SignalProducerwill fail. E.g., if
retry(1)is
used in the example above instead of
retry(2),
"Signal Error"will be printed instead of
"Success".
Mapping
errors
The mapErroroperator transforms any error in an event stream into a new error.
enum CustomError: String, ErrorType { case Foo = "Foo" case Bar = "Bar" case Other = "Other" var nsError: NSError { return NSError(domain: "CustomError.\(rawValue)", code: 0, userInfo: nil) } var description: String { return "\(rawValue) Error" } } let (signal, sink) = Signal<String, NSError>.pipe() signal |> mapError { (error: NSError) -> CustomError in switch error.domain { case "com.example.foo": return .Foo case "com.example.bar": return .Bar default: return .Other } } |> observe(error: println) sendError(sink, NSError(domain: "com.example.foo", code: 42, userInfo: nil)) // prints "Foo Error"
Promote
The promoteErrorsoperator promotes an event stream that does not generate errors into one that can.
let (numbersSignal, numbersSink) = Signal<Int, NoError>.pipe() let (lettersSignal, lettersSink) = Signal<String, NSError>.pipe() numbersSignal |> promoteErrors(NSError) |> combineLatestWith(lettersSignal)
The given stream will still not actually generate errors, but this is useful because some operators to combine
streamsrequire the inputs to have matching error types.
This document explains some of the most common operators used in ReactiveCocoa, and includes examples demonstrating their use.
Operators that apply to sequences and signals are
known as stream operators.
Performing
side effects with signals
Subscription
Injecting
effects
Transforming
streams
Mapping
Filtering
Combining
streams
Concatenating
Flattening
Mapping
and flattening
Combining
signals
Sequencing
Merging
Combining
latest values
Switching
Performing
side effects with signals
Most signals start out "cold," which means that they will not do any work until subscription.Upon subscription, a signal or its subscribers can
perform side effects, like logging to the console, making a network request, updating the user interface, etc.
Side effects can also be injected into
a signal, where they won't be performed immediately, but will instead take effect with each subscription later.
Subscription
The -subscribe… methods give youaccess to the current and future values in a signal:
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; // Outputs: A B C D E F G H I [letters subscribeNext:^(NSString *x) { NSLog(@"%@", x); }];
For a cold signal, side effects will be performed once per subscription:
__block unsigned subscriptions = 0; RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { subscriptions++; [subscriber sendCompleted]; return nil; }]; // Outputs: // subscription 1 [loggingSignal subscribeCompleted:^{ NSLog(@"subscription %u", subscriptions); }]; // Outputs: // subscription 2 [loggingSignal subscribeCompleted:^{ NSLog(@"subscription %u", subscriptions); }];
This behavior can be changed using a connection.
Injecting
effects
The -do… methods addside effects to a signal without actually subscribing to it:
__block unsigned subscriptions = 0; RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { subscriptions++; [subscriber sendCompleted]; return nil; }]; // Does not output anything yet loggingSignal = [loggingSignal doCompleted:^{ NSLog(@"about to complete subscription %u", subscriptions); }]; // Outputs: // about to complete subscription 1 // subscription 1 [loggingSignal subscribeCompleted:^{ NSLog(@"subscription %u", subscriptions); }];
Transforming
streams
These operators transform a single stream into a new stream.
Mapping
The -map: method is used to transformthe values in a stream, and create a new stream with the results:
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; // Contains: AA BB CC DD EE FF GG HH II RACSequence *mapped = [letters map:^(NSString *value) { return [value stringByAppendingString:value]; }];
Filtering
The -filter: method uses a blockto test each value, including it into the resulting stream only if the test passes:
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: 2 4 6 8 RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) { return (value.intValue % 2) == 0; }];
Combining
streams
These operators combine multiple streams into a single new stream.
Concatenating
The -concat: method appends onestream's values to another:
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9 RACSequence *concatenated = [letters concat:numbers];
Flattening
The -flatten operator is appliedto a stream-of-streams, and combines their values into a single new stream.
Sequences are concatenated:
RACSequence *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence; RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; RACSequence *sequenceOfSequences = @[ letters, numbers ].rac_sequence; // Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9 RACSequence *flattened = [sequenceOfSequences flatten];
Signals are merged:
RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSignal *signalOfSignals = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) { [subscriber sendNext:letters]; [subscriber sendNext:numbers]; [subscriber sendCompleted]; return nil; }]; RACSignal *flattened = [signalOfSignals flatten]; // Outputs: A 1 B C 2 [flattened subscribeNext:^(NSString *x) { NSLog(@"%@", x); }]; [letters sendNext:@"A"]; [numbers sendNext:@"1"]; [letters sendNext:@"B"]; [letters sendNext:@"C"]; [numbers sendNext:@"2"];
Mapping
and flattening
Flattening isn't thatinteresting on its own, but understanding how it works is important for -flattenMap:.
-flattenMap:is used to transform each of a stream's values into a new stream. Then, all of
the streams returned will be flattened down into a single stream. In other words, it's -map: followed
by -flatten.
This can be used to extend or edit sequences:
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence; // Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 RACSequence *extended = [numbers flattenMap:^(NSString *num) { return @[ num, num ].rac_sequence; }]; // Contains: 1_ 3_ 5_ 7_ 9_ RACSequence *edited = [numbers flattenMap:^(NSString *num) { if (num.intValue % 2 == 0) { return [RACSequence empty]; } else { NSString *newNum = [num stringByAppendingString:@"_"]; return [RACSequence return:newNum]; } }];
Or create multiple signals of work which are automatically recombined:
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; [[letters flattenMap:^(NSString *letter) { return [database saveEntriesForLetter:letter]; }] subscribeCompleted:^{ NSLog(@"All database entries saved successfully."); }];
Combining
signals
These operators combine multiple signals into a single new RACSignal.
Sequencing
-then: starts the originalsignal, waits for it to complete, and then only forwards the values from a new signal:
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal; // The new signal only contains: 1 2 3 4 5 6 7 8 9 // // But when subscribed to, it also outputs: A B C D E F G H I RACSignal *sequenced = [[letters doNext:^(NSString *letter) { NSLog(@"%@", letter); }] then:^{ return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal; }];
This is most useful for executing all the side effects of one signal, then starting another, and only returning the second signal's values.
Merging
The +merge: method willforward the values from many signals into a single stream, as soon as those values arrive:
RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSignal *merged = [RACSignal merge:@[ letters, numbers ]]; // Outputs: A 1 B C 2 [merged subscribeNext:^(NSString *x) { NSLog(@"%@", x); }]; [letters sendNext:@"A"]; [numbers sendNext:@"1"]; [letters sendNext:@"B"]; [letters sendNext:@"C"]; [numbers sendNext:@"2"];
Combining
latest values
The +combineLatest: and +combineLatest:reduce:methods
will watch multiple signals for changes, and then send the latest values from all of them when a change occurs:
RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSignal *combined = [RACSignal combineLatest:@[ letters, numbers ] reduce:^(NSString *letter, NSString *number) { return [letter stringByAppendingString:number]; }]; // Outputs: B1 B2 C2 C3 [combined subscribeNext:^(id x) { NSLog(@"%@", x); }]; [letters sendNext:@"A"]; [letters sendNext:@"B"]; [numbers sendNext:@"1"]; [numbers sendNext:@"2"]; [letters sendNext:@"C"]; [numbers sendNext:@"3"];
Note that the combined signal will only send its first value when all of the inputs have sent at least one. In the example above,
@"A"was
never forwarded because
numbershad not sent a value yet.
Switching
The -switchToLatest operatoris applied to a signal-of-signals, and always forwards the values from the latest signal:
RACSubject *letters = [RACSubject subject]; RACSubject *numbers = [RACSubject subject]; RACSubject *signalOfSignals = [RACSubject subject]; RACSignal *switched = [signalOfSignals switchToLatest]; // Outputs: A B 1 D [switched subscribeNext:^(NSString *x) { NSLog(@"%@", x); }]; [signalOfSignals sendNext:letters]; [letters sendNext:@"A"]; [letters sendNext:@"B"]; [signalOfSignals sendNext:numbers]; [letters sendNext:@"C"]; [numbers sendNext:@"1"]; [signalOfSignals sendNext:letters]; [numbers sendNext:@"2"]; [letters sendNext:@"D"];
相关文章推荐
- Hadoop Archive解决海量小文件存储
- WPF如何实现窗体透明——Opacity
- 关于Linux下的iptables
- openlayers3使用总结
- 安装交叉编译器arm-none-linux-gnueabi-gcc
- Unix/Linux 的 shell
- Unix/Linux 的 shell
- Unix/Linux 的 shell
- Unix/Linux 的 shell
- Thinkphp 3.2 Nginx 伪静态写法15年7月最新有效
- Bumblebee解决Linux显卡问题 安装过后出现运行失败问题
- linux端口占用
- linux编译内核2.6.32.67
- 分析Linux内核创建一个新进程的过程
- Xampp中Apache无法启动的解决方法
- 分析Linux内核创建一个新进程的过程
- RetHat6.5 Nginx安装
- 游戏服务端架构发展史(上)
- 【CSAPP】Shell Lab 外壳实验
- 协同过滤之 二、SlopeOne推荐算法