- I’ve covered it in two previous articles
ReactiveCocoa
Some of the insights and uses of “The” are not introduced here - The first article describes ReactiveCocoa’s use of RACSingle in detail
- The second article introduces the detailed usage of ReactiveCocoa collection
- If you are interested, you can read these two articles first
- Let’s focus on some of them
Map
.concat
Advanced usage
I. Introduction to common operations of ReactiveCocoa
1. ReactiveCocoa Operation Instructions
- All the signals
RACSignal
Because all operation methods are defined in racstream.h, you can simply inherit RACStream and have operation handling methods.
2. Operation idea of ReactiveCocoa
- Hook is a technique used to change the results of API(application programming interface: method) execution.
- Hook uses: techniques for intercepting API calls.
- Hook principle: Execute your own method and change the output of the result each time you call an API and return the result
Ii. Advanced operations
1. ReactiveCocoa core method bind
ReactiveCocoa
The core method of operation isbind
In RAC, the core development mode is also binding. The previous development mode is assignment. In RAC, the focus should be on binding, which means that when you create an object, you can bind it to what you want to do later, rather than waiting for assignment to do later.- The bind method is rarely used in development. Bind belongs to the low-level method in RAC, which has encapsulated many useful methods.
- The bind method is briefly introduced and used
- Requirement: Listen for the content of the text box, each time output, after the content of the string
"jun"
And displayed inlabel
on
- Requirement: Listen for the content of the text box, each time output, after the content of the string
Method 1: Concatenate the string after the result is returned
@weakify(self)
[_textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
@strongify(self)
self.showLabel.text = [NSString stringWithFormat:@ % @ + % @ "", x, @"jun"];
}];
Copy the code
Method 2: Before returning the result, concatenate the string, using bind
[[_textField.rac_textSignal bind:^RACSignalBindBlock _Nonnull{
return ^RACSignal *(id value, BOOL *stop){
return [RACReturnSignal return: [NSString stringWithFormat:"Output: %@", value]];
};
}] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
Copy the code
bind
Methods to introducebind
Method argument: a return value is passed inRACStreamBindBlock
theblock
parameterRACStreamBindBlock
Is ablock
The return value is signal, parameter(the value, the stop)
, so the parameterblock
The return value is also oneblock
- As follows:
typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);
Copy the code
RACStreamBindBlock
:- Parameter one (value): indicates that the original value of the received signal has not been processed
- Parameters of the two
*stop
: used to control bindingBlock
If the*stop
= yes, then the binding ends. - Return value: signal, processed, returned through this signal, generally used
RACReturnSignal
, you need to manually import the header fileRACReturnSignal.h
@interface RACReturnSignal<__covariant ValueType> : RACSignal<ValueType>
+ (RACSignal<ValueType> *)return:(ValueType)value;
@end
Copy the code
- The bind method uses the following steps:
- Pass in a return value
RACStreamBindBlock
theblock
- Describe a
RACStreamBindBlock
The type ofbindBlock
As ablock
The return value of. - Describes a signal that returns a result, as
bindBlock
The return value of. - Note: in
bindBlock
To do signal result processing
- Pass in a return value
- Bind low-level implementation:
- The source signal calls bind, which recreates a bind signal.
- When a binding signal is subscribed, it is called in the binding signal
didSubscribe
, generate abindingBlock
. - When the source signal has something to emit, it passes the content to
bindingBlock
Process, callbindingBlock(value,stop)
- call
bindingBlock(value,stop)
, returns a signal that the content processing is complete(RACReturnSignal)
. - To subscribe to
RACReturnSignal
, it will get the subscriber of the binding signal and send out the signal content after processing
2. The Map (flattenMap, Map)
flattenMap
.Map
Used to map source signal content to new content- in
Swift
See this article for more details about the use of these functionsSwift functional programming of Map&Reduce&Filter
2-1. flattenMap
Maps the contents of the source signal to a new signal, which can be of any type
flattenMap
Use steps:- Pass in a
block
.block
The type is the return valueRACStream
, the parametervalue
- parameter
value
Is the content of the source signal, get the content of the source signal to do processing - Packaged in
RACReturnSignal
Signal, go back out
- Pass in a
flattenMap
Underlying implementation:- 0.
flattenMap
Internal callsbind
Method implementation,flattenMap
In theblock
Is returned as the value ofbind
In thebindBlock
The return value of. - 1. When subscribing to a binding signal, it is generated
bindBlock
. - 2. Called when the source signal sends content
bindBlock(value, *stop)
- 3. Call
bindBlock
, will be called internallyflattenMap
theblock
.flattenMap
theblock
Function: it is to package the processed data into signals - 4. The returned signal will eventually act as
bindBlock
Return signal, asbindBlock
Return signal of. - 5. To subscribe to
bindBlock
Will get the subscriber of the binding signal, and send out the signal content that has been processed
- 0.
- (__kindof RACStream *)flattenMap:(__kindof RACStream * (^)(id value))block {
Class class = self.class;
return [[self bind:^{
return^ (id value, BOOL *stop) {
idstream = block(value) ? : [class empty];
NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);
return stream;
};
}] setNameWithFormat:@"[%@] -flattenMap:".self.name];
}
Copy the code
Simple to use
@weakify(self)
[[_textField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {
// This block is called when the source signal is emitted.
// Return value: bind the contents of the signal.
return [RACReturnSignal return: [NSString stringWithFormat:"Flat output: %@", value]];
}] subscribeNext:^(id _Nullable x) {
@strongify(self)
// Subscribe to the binding signal. Every time the original signal sends content, after processing, this black will be called
self.showLabel.text = x;
NSLog(@ "% @", x);
}];
Copy the code
2-2. Map
Map function: maps the value of the source signal to a new value
Map
Use steps:- Pass in a
block
, type is return object, parameter isvalue
value
Is the content of the source signal, directly get the content of the source signal to do processing- I’m just going to return it, instead of wrapping it up as a signal, and the value returned is the value of the mapping.
- Pass in a
Map
Underlying implementation:Map
The bottom line is actually callingflatternMap
, a Map ofblock
The value returned by theflatternMap
Block in.- When the subscription binding signal is generated
bindBlock
. - Called when the source signal sends the content
bindBlock(value, *stop)
- call
bindBlock
, will be called internallyflattenMap
theblock
flattenMap
theblock
Internally it callsMap
In theblock
, put the Mapblock
The returned content is wrapped as the returned signal.- The returned signal will eventually act as
bindBlock
Return signal, asbindBlock
Return signal of. - To subscribe to
bindBlock
Will get the subscriber of the binding signal, and send out the signal content that has been processed
- (__kindof RACStream *)map:(id(^) (id value))block {
NSCParameterAssert(block ! =nil);
Class class = self.class;
return [[self flattenMap:^(id value) {
return [class return:block(value)];
}] setNameWithFormat:@"[%@] -map:".self.name];
}
Copy the code
Simple to use
//Map
[[_textField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {
return [NSString stringWithFormat:@"map output: %@", value];
}] subscribeNext:^(id _Nullable x) {
@strongify(self)
self.showLabel.text = x;
NSLog(@ "% @", x);
}];
// Array processing
NSArray *arr = @[@ "2".@ "3".@"a".@"g"];
RACSequence *sequence = [arr.rac_sequence map:^id _Nullable(id _Nullable value) {
return [NSString stringWithFormat:@ % @ - "", value];
}];
NSLog(@ "% @", [sequence array]);
/ * output: 2018-03-24 14:13:32. 421337 + 0800 ReactiveObjc [9043-492929] (" - 2 - ", "- 3 -", "- a -", "- g -") * /
Copy the code
2-3. FlatternMap
和 Map
The difference between
FlatternMap
In theBlock
Return signal.Map
In theBlock
Returns an object.- In development, if the value emitted by the signal is not a signal, the mapping is generally used
Map
- In development, if the value emitted by the signal is a signal, the mapping is generally used
FlatternMap
- Signal of signal
- When one signal needs to return a value from another signal
- Let’s take a look at this example
Pragma signals within signals
- (void)singleAndSingle {
// Create signal within signal
RACSubject *sonSingle = [RACSubject subject];
RACSubject *single = [RACSubject subject];
[[sonSingle flattenMap:^__kindof RACSignal * _Nullable(id _Nullable value) {
//sonSingle is called when it sends a signal
return value;
}] subscribeNext:^(id _Nullable x) {
// sonSingle will be called only when sonSingle's child signal is a message
NSLog("Output: %@", x);
}];
// A signal within a signal sends a subsignal
[sonSingle sendNext:single];
// The subsignal sends the content
[single sendNext:@123];
}
Copy the code
Combination of 3.
3-1. concat
Concatenation of signals in a fixed sequence. When multiple signals are sent, sequential signals are received
// Let's take a look at the normal operation
- (void)setConcatAction {
// Execute A first, then B
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACReplaySubject subject];
NSMutableArray *array = [NSMutableArray array];
// Subscribe signal
[subjectA subscribeNext:^(id _Nullable x) {
[array addObject:x];
}];
[subjectB subscribeNext:^(id _Nullable x) {
[array addObject:x];
}];
// Send a signal
[subjectB sendNext:@"B"];
[subjectA sendNext:@"A"];
[subjectA sendCompleted];
// output: [B, A]
NSLog(@ "% @", array);
}
Copy the code
- Obviously, the above results do not meet our requirements: restrict A, perform B
- Let’s look at usage
concat
After the implementation
- (void)setConcatAction {
// Execute A first, then B
RACSubject *subC = [RACSubject subject];
RACSubject *subD = [RACReplaySubject subject];
NSMutableArray *array2 = [NSMutableArray array];
// Subscribe signal
[[subC concat:subD] subscribeNext:^(id _Nullable x) {
[array2 addObject:x];
}];
// Send a signal
[subD sendNext:@"D"];
[subC sendNext:@"C"];
[subC sendCompleted];
// output: [C, D]
NSLog(@ "% @", array2);
}
Copy the code
- As you can see, the output is as expected, in sequence
- So,
concat
How exactly is the bottom layer implemented?
- (RACSignal *)concat:(RACSignal *)signal {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACCompoundDisposable *compoundDisposable = [[RACCompoundDisposable alloc] init];
RACDisposable *sourceDisposable = [self subscribeNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
RACDisposable *concattedDisposable = [signal subscribe:subscriber];
[compoundDisposable addDisposable:concattedDisposable];
}];
[compoundDisposable addDisposable:sourceDisposable];
return compoundDisposable;
}] setNameWithFormat:@"[%@] -concat: %@".self.name, signal];
}
Copy the code
concat
Underlying implementation:- When a concatenation signal is subscribed, the concatenation signal is called
didSubscribe
didSubscribe
The first source signal is subscribed tosubjectA
- The first source signal is executed
subjectA
thedidSubscribe
- The first source signal
subjectA
thedidSubscribe
, the first source signal is calledsubjectA
The subscriber’snextBlock
The value is sent out by the subscriber of the concatenated signal. - The first source signal
subjectA
thedidSubscribe
The first source signal is called when thesubjectA
The subscriber’scompletedBlock
Subscribe to the second source signalsubjectB
That’s when it’s activatedsubjectB
- Subscribe to the second source signal
subjectB
Execute the second source messagesubjectB
The no.didSubscribe
- Second source signal
subjectB
thedidSubscribe
The value is sent through the subscriber of the concatenated signal.
- When a concatenation signal is subscribed, the concatenation signal is called
3-2. then
Used to join two signals. When the first signal is complete, the signal returned by then is joined
- (RACSignal *)then:(RACSignal * (^)(void))block {
NSCParameterAssert(block ! =nil);
return [[[self
ignoreValues]
concat:[RACSignal defer:block]]
setNameWithFormat:@"[%@] -then:".self.name];
}
//ignoreValues underlying implementation
- (RACSignal *)ignoreValues {
return [[self filter:^(id _) {
return NO;
}] setNameWithFormat:@"[%@] -ignoreValues".self.name];
}
Copy the code
- Realize the principle of
- The underlying calls
filter
Filter out the values emitted by its own signal (filter
More on that later.) - And then use it
concat
The connectionthen
Returned signal - Here are the test cases
- The underlying calls
- (void)setThenAction {
RACSubject *subjectA = [RACReplaySubject subject];
RACSubject *subjectB = [RACReplaySubject subject];
// Send a signal
[subjectA sendNext:@"A"];
[subjectA sendCompleted];
[subjectB sendNext:@"B"];
// Subscribe signal
[[subjectA then:^RACSignal * _Nonnull{
return subjectB;
}] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
// only: B will be printed
// Will not output: A
}
Copy the code
3-3. merge
Merges multiple signals into a single signal, which is called when any signal has a new value
- (RACSignal *)merge:(RACSignal *)signal {
return [[RACSignal
merge:@[ self, signal ]]
setNameWithFormat:@"[%@] -merge: %@".self.name, signal];
}
+ (RACSignal *)merge:(id<NSFastEnumeration>)signals {
NSMutableArray *copiedSignals = [[NSMutableArray alloc] init];
for (RACSignal *signal in signals) {
[copiedSignals addObject:signal];
}
return [[[RACSignal
createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
for (RACSignal *signal in copiedSignals) {
[subscriber sendNext:signal];
}
[subscriber sendCompleted];
return nil;
}]
flatten]
setNameWithFormat:@"+merge: %@", copiedSignals];
}
Copy the code
- The underlying implementation
- 1. When the merge signal is subscribed, all the signals are traversed and emitted.
- 2. Every time a signal is sent, it will be subscribed
- 3. Once a merge signal is subscribed, all the signals in it will be subscribed.
- 4. As soon as a signal is sent, it will be monitored.
- (void)setMergeAction {
// Just want to disordered integration of signal data
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACSubject subject];
RACSubject *subjectC = [RACSubject subject];
// Merge signals
RACSignal *single = [[subjectA merge:subjectB] merge:subjectC];
// Subscribe signal
[single subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
// Send a message
[subjectA sendNext:@"A"];
[subjectC sendNext:@"C"];
[subjectB sendNext:@"B"];
}
// Output A, C, B
Copy the code
3-4. zipWith
Compressing two signals into one will trigger the next event of the compressed stream only if both signals emit signal contents at the same time and the contents of the two signals are combined into a tuple
- Underlying implementation:
- 1. Define the compressed signal and the internal will automatically subscribe
subjectA
.subjectB
- 2. Whenever
subjectA
orsubjectB
Give a signal, and you will judgesubjectA
.subjectB
If a signal is sent, it will pack the most recent signal into a tuple.
- 1. Define the compressed signal and the internal will automatically subscribe
- (void)setZipwithAction {
// Just want to disordered integration of signal data
RACSubject *subjectA = [RACSubject subject];
RACSubject *subjectB = [RACSubject subject];
// Merge signals
RACSignal *single = [subjectA zipWith:subjectB];
// Subscribe signal
[single subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
// Send a message
[subjectA sendNext:@"A"];
[subjectB sendNext:@"B"];
(A, B) */
}
Copy the code
3-5. combineLatest
- Combine multiple signals and get the latest values for each signal
- Each merged signal must be at least once
sendNext
, will trigger the merge signal - Here we consider the requirement that, on the login page, the login button can only be clicked if both the account and password are entered, otherwise it cannot be clicked
- Normally we want to listen for input from each text box
- So let’s see
combineLatest
Controls whether the login button is clickable
- (void)setCombineLatest {
// Combine two signals into a single signal, just like zip
RACSignal *single = [_accountText.rac_textSignal combineLatestWith:_passwordText.rac_textSignal];
[single subscribeNext:^(id _Nullable x) {
RACTupleUnpack(NSString *account, NSString *password) = x;
_loginButton.enabled = account.length > 0 && password.length > 0;
}];
}
Copy the code
- Underlying implementation:
- 1. When the combination signal is subscribed, the internal will automatically subscribe to two signals, both signals must send content, will be triggered. (Zip, on the other hand, is triggered only when two signals are sent out.)
- 2. Combine the two signals into a tuple.
3-6. reduce
Aggregation: Used for signaling tuples, aggregating the values of signaling tuples into one value
Here we optimize the above code using a RACSingle class method
- (void)setReduceAction {
// reduce: aggregates the values of multiple signals into a single value
RACSignal *single = [RACSignal combineLatest:@[_accountText.rac_textSignal, _passwordText.rac_textSignal] reduce:^id (NSString *account, NSString *password){
return @(account.length > 0 && password.length > 0);
}];
[single subscribeNext:^(id _Nullable x) {
_loginButton.enabled = [x boolValue];
}];
}
Copy the code
RACSingle
Class method- A parameter:
(id<NSFastEnumeration>)
typeNSFastEnumeration
In our last articleReactiveCocoa collection usage details 02A brief introduction to theNSFastEnumeration
: is a protocol that all classes that follow this protocol can be treated as an array, for exampleNSArray
- So here, you should pass an include
RACSingle
Array of signals
- Parameters of the two:
(RACGenericReduceBlock)reduceBlock
Is ablack
- A parameter:
typedef ValueType _Nonnull (^RACGenericReduceBlock)();
// ReduceBLCOK parameters, how many signal combinations, reduceBLcoK has how many parameters, each parameter is the previous signal sent content
Copy the code
I’m going to use a macro here, and I really need to simplify this code
- (void)setReduceAction {
RAC(_loginButton, enabled) = [RACSignal combineLatest:@[_accountText.rac_textSignal, _passwordText.rac_textSignal] reduce:^id (NSString *account, NSString *password){
return @(account.length > 0 && password.length > 0);
}];
}
Copy the code
Here in a macro RAC, temporarily not go into, will focus on organizing your RAC macros, concrete implementation is as follows
#define RAC(TARGET, ...) \
metamacro_if_eq(1, metamacro_argcount(__VA_ARGS__)) \
(RAC_(TARGET, __VA_ARGS__, nil)) \
(RAC_(TARGET, __VA_ARGS__))
/// Do not use this directly. Use the RAC macro above.
#define RAC_(TARGET, KEYPATH, NILVALUE) \
[[RACSubscriptingAssignmentTrampoline alloc] initWithTarget:(TARGET) nilValue:(NILVALUE)][@keypath(TARGET, KEYPATH)]
Copy the code
4. The filter
4-1. filter
Filter signals. Filter out signals that do not meet the conditions
- (void) filterAction{
//filter
// Intercepts characters equal to 11 bits
[[_accountText.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
// For cell phone numbers, return true only if the number equals 11 digits
return value.length == 11;
}]subscribeNext:^(NSString * _Nullable x) {
// Only characters equal to 11 bits are returned
NSLog(@"filter = %@", x);
}];
}
Copy the code
The underlying filter is the flatMap method called as follows:
- (__kindof RACStream *)filter:(BOOL(^) (id value))block {
NSCParameterAssert(block ! =nil);
Class class = self.class;
return [[self flattenMap:^ id (id value) {
if (block(value)) {
return [class return:value];
} else {
return class.empty;
}
}] setNameWithFormat:@"[%@] -filter:".self.name];
}
Copy the code
4-2. ignore
A signal that ignores certain values
- (void)setIgnoreAction {
///ignore
// Only the first character bit: m can be tested
[[_accountText.rac_textSignal ignore:@"m"] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"ignore = %@", x);
}];
//ignoreValues: Ignore all signals
[[_passwordText.rac_textSignal ignoreValues] subscribeNext:^(id _Nullable x) {
NSLog(@"allIgnore = %@", x);
}];
}
Copy the code
At the bottom of the ignore method are the filter methods that are called
//ignore
- (__kindof RACStream *)ignore:(id)value {
return [[self filter:^ BOOL (id innerValue) {
returninnerValue ! = value && ! [innerValue isEqual:value]; }] setNameWithFormat:@"[%@] -ignore: %@".self.name, RACDescription(value)];
}
//ignoreValues
- (RACSignal *)ignoreValues {
return [[self filter:^(id _) {
return NO;
}] setNameWithFormat:@"[%@] -ignoreValues".self.name];
}
Copy the code
4-3. distinctUntilChanged
- Signals are emitted when there is a significant change between the previous value and the current value, otherwise it will be ignored.
- In development, refreshing the UI is used frequently, only twice when the data is different
//distinctUntilChanged
- (void)setdistinctUntilChanged {
// Create a signal
RACSubject *subject = [RACSubject subject];
/ / subscribe
[[subject distinctUntilChanged] subscribeNext:^(id _Nullable x) {
NSLog(@"distinctUntilChanged = %@", x);
}];
[subject sendNext:@12];
[subject sendNext:@12];
[subject sendNext:@23];
DistinctUntilChanged = 12 distinctUntilChanged = 23 */
}
Copy the code
DistinctUntilChanged underneath is a high-level use of bind for the call
- (__kindof RACStream *)distinctUntilChanged {
Class class = self.class;
return [[self bind:^{
__block id lastValue = nil;
__block BOOL initial = YES;
return^ (id x, BOOL *stop) {
if(! initial && (lastValue == x || [x isEqual:lastValue]))return [class empty];
initial = NO;
lastValue = x;
return [class return:x];
};
}] setNameWithFormat:@"[%@] -distinctUntilChanged".self.name];
}
Copy the code
4-4. take
The sendCompleted statement stops sending signals prematurely when it encounters the sendCompleted statement execution
- (void)setTakeAndTakeLast {
//take
RACSubject *subject1 = [RACSubject subject];
[[subject1 take:2] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
[subject1 sendNext:@1];
[subject1 sendNext:@2];
[subject1 sendCompleted];
[subject1 sendNext:@3];
// Outputs: 1, 2 respectively
}
// If the code above sends the signal is adjusted to
[subject1 sendNext:@1];
[subject1 sendCompleted];
[subject1 sendNext:@2];
[subject1 sendNext:@3];
// Then the output will be: 1
Copy the code
4-5. takeLast
Get N signals before calling sendCompleted, provided that the subscriber must call sendCompleted or nothing will be done
- (void)setTakeAndTakeLast {
//takeLast
RACSubject *subject1 = [RACSubject subject];
[[subject1 takeLast:2] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
[subject1 sendNext:@1];
[subject1 sendNext:@2];
[subject1 sendNext:@3];
[subject1 sendCompleted];
}
Copy the code
4-6. takeUntil
Once the incoming signal has finished sending or the subject2 has started sending the signal, the signal content will not be received
- (void)setTakeAndTakeLast {
//takeUntil
RACSubject *subject1 = [RACSubject subject];
RACSubject *subject2 = [RACSubject subject];
[[subject1 takeUntil:subject2] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
[subject1 sendNext:@11];
[subject1 sendNext:@12];
// [subject1 sendCompleted];
[subject1 sendNext:@13];
[subject2 sendNext:@ "21"];
[subject2 sendNext:@ "22"];
// Output: 11, 12, 13
// When sendCompleted is uncommented, only: 11, 12 is printed
}
Copy the code
4-7. switchToLatest
- A signal that is used primarily for signals, and sometimes emits signals, will pick up the latest signal sent in the signal’s signal
- The underlying method is called
flattenMap
methods
- (RACSignal *)switchToLatest {
return [[RACSignal createSignal:^(id<RACSubscriber> subscriber) {
RACMulticastConnection *connection = [self publish];
RACDisposable *subscriptionDisposable = [[connection.signal
flattenMap:^(RACSignal *x) {
NSCAssert(x == nil || [x isKindOfClass:RACSignal.class], @"-switchToLatest requires that the source signal (%@) send signals. Instead we got: %@".self, x);
// -concat:[RACSignal never] prevents completion of the receiver from
// prematurely terminating the inner signal.
return [x takeUntil:[connection.signal concat:[RACSignal never]]];
}]
subscribe:subscriber];
RACDisposable *connectionDisposable = [connection connect];
return [RACDisposable disposableWithBlock:^{
[subscriptionDisposable dispose];
[connectionDisposable dispose];
}];
}] setNameWithFormat:@"[%@] -switchToLatest".self.name];
}
Copy the code
Let’s take a look at an example
- (void)setswitchToLatest {
// The signal of the signal
RACSubject *subject1 = [RACSubject subject];
RACSubject *subject2 = [RACSubject subject];
// Get the latest signal in signal, subscribe to the latest signal
[[subject1 switchToLatest] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
// Send a signal
[subject1 sendNext:subject2];
[subject2 sendNext:@" Signal of signal"];
// Final result output: "signal of signal"
}
Copy the code
4-8. skip
Skip N signals before subscribing to signals
- (void)setSkipAction {
// Create a signal
RACSubject *subject = [RACSubject subject];
// Subscribe signal
// Skip two signals
[[subject skip:2] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
// Send a signal
[subject sendNext:@1];
[subject sendNext:@2];
[subject sendNext:@3];
[subject sendNext:@4];
// Because two signals are skipped above, only 3, 4 are printed here
}
Copy the code
5. Perform scheduled operations
5-1. interval
A timer that sends out signals at regular intervals
// the RAC timer is executed at intervals
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@timer);
}];
Copy the code
RACScheduler is a class that manages threads in RAC
5-2. delay
The signal is sent after a delay of some time
//delay: delay execution
[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@"delay"];
return nil;
}] delay:2] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
Copy the code
5-3. timeout
Timeout, you can make a signal in a certain time after the automatic error
//timeout: allows a signal to automatically report an error after a certain period of time
RACSignal *single = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
return nil;
}] timeout:2 onScheduler:[RACScheduler currentScheduler]];
[single subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
} error:^(NSError * _Nullable error) {
// Automatically called after 2 seconds
NSLog(@ "% @", error);
}];
Copy the code
6. Repeat the operation
6-1. retry
Retry: At any failure, the block in the create signal is re-executed until it succeeds.
- (void)setResertAction {
//retry
__block int i = 0;
[[[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
if (i == 5) {
[subscriber sendNext:@12];
} else {
NSLog(@" Error occurred");
[subscriber sendError:nil];
}
i++;
return nil;
}] retry] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
} error:^(NSError * _Nullable error) {
NSLog(@ "% @", error);
}];
2018-03-30 15:44:08.412860+0800 ReactiveObjc[4125:341376] error 2018-03-30 15:44:08.461105+0800 An error occurred in ReactiveObjc[4125:341376] 2018-03-30 15:44:08.461897+0800 ReactiveObjc[4125:341376] 2018-03-30 15:44:08.462478+0800 ReactiveObjc[4125:341376] has an error 2018-03-30 15:44:08.462913+0800 ReactiveObjc[4125:341376] has an error 2018-03-30 15:44:08.463351+0800 ReactiveObjc[4125:341376] 12 */
}
Copy the code
6-2. replay
Replay: When a signal is subscribed multiple times, the content is played repeatedly
//replay
RACSignal *single = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@23];
[subscriber sendNext:@34];
return nil;
}] replay];
[single subscribeNext:^(id _Nullable x) {
NSLog(@" First subscription -%@", x);
}];
[single subscribeNext:^(id _Nullable x) {
NSLog(@" Second subscription -%@", x);
}];
/* Output result: 2018-03-30 15:51:20.115052+0800 ReactiveObjc[4269:361568] first subscription -23 2018-03-30 15:51:20.115195+0800 ReactiveObjc[4269:361568] First subscription -34 2018-03-30 15:51:20.115278+0800 ReactiveObjc[4269:361568] second subscription -23 2018-03-30 15:51:20.115352+0800 ReactiveObjc[4269:361568] Second subscription -34 */
Copy the code
6-3. throttle
Throttling: When a signal is sent frequently, the throttling can be used. After a certain period of time (1 second), no signal content is received. After this period (1 second), the last signal content is sent.
RACSubject *subject = [RACSubject subject];
[[subject throttle:0.001] subscribeNext:^(id _Nullable x) {
NSLog(@ "% @", x);
}];
[subject sendNext:@10];
[subject sendNext:@11];
[subject sendNext:@12];
[subject sendNext:@13];
[subject sendNext:@14];
[subject sendNext:@15];
[subject sendNext:@16];
[subject sendNext:@17];
[subject sendNext:@18];
// The execution speed is very fast, so there is only one final output: 18
Copy the code
- These are some examples and explanations of common advanced usage in RAC
- If there are shortcomings, please give me more advice, and we will continue to update the relevant knowledge points in the later period
- Here are two articles on RAC
- ReactiveCocoa Usage details 01
- ReactiveCocoa collection usage details 02