ReactiveCocoa 基本操作符

最近打算好好学习下ReactiveCocoa,个人认为最好的学习方式就是看官方文档。这篇基本操作符的介绍,也是翻译自官方文档

基本操作符

本文档解释了ReactiveCocoa中最常用的操作符,并包含了演示其用法的的示例
适用于sequencessignal的操作符被称为stream的操作符

执行信号产生的副作用

大多数信号都是冷信号,这意味着直到被订阅它们才会生效。
订阅后,信号或者他的订阅者可以执行,例如打印到控制台、发起网络请求、更新UI等操作。

###订阅(Subscription)

-subscribe系列方法可以访问signal的当前以及未来的值:

1
2
3
4
5
6
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);
}];

对于冷信号来说,每一次订阅都将执行一次副作用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__block unsigned subscriptions = 0;

RACSignal *loggingSignal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
subscriptions++;
[subscriber sendCompleted];
return nil;
}];

// Outputs:
// subscription 1
[loggingSignal subscribeCompleted:^{
NSLog(@"subscriptions %lu", subscriptions);
}];

// Outputs:
// subscription 2
[loggingSignal subscribeCompleted:^{
NSLog(@"subscriptions %lu", subscriptions);
}];

这种行为可以使用过connection来改变。

###注入副作用(Injecting effect)

-do系列方法可以给signal添加副作用,而不需要订阅

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
__block unsigned subscriptions = 0;

RACSignal *loggingSignal = [RACSignal createSignal:^RACDisposable *(id <RACSubscriber> subscriber) {
subscriptions++;
[subscriber sendCompleted];
return nil;
}];

// Does not output anything yet
[loggingSignal doCompleted:^{
NSLog(@"about to complete subscription %u", subscriptions);
}];

// Outputs:
// about to complete subscription 1
// subscription 1
[loggingSignal subscribeCompleted:^{
NSLog(@"subscriptions %lu", subscriptions);
}];

变换Streams

变换操作符可以将单个stream变换成一个新的stream

映射(Mapping)

-map:方法用于将stream中的值转换,并基于转换后的新值创建一个新的stream

1
2
3
4
5
6
7
RACSequence *letters = [@"A, B,C,D,E,F G,H, I" componentsSeparatedByString:@" "].rac_sequence;

// Contains: AA BB CC DD EE FF GG HH II
[letters map:^id(NSString * value) {
value = [value stringByAppendingString:value];
return value;
}];

过滤(Filtering)

-filter:方法使用一个block,来筛选出符合条件的值,并生成新的stream

1
2
3
4
5
6
RACSequence *numbers = [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence;

// Contains: 2 4 6 8
RACSequence *filtered = [numbers filter:^BOOL(NSNumber * value) {
return value.intValue % 2 == 0;
}];

结合流(Combining Streams)

结合操作符可以将多个stream合并成单个stream

连接(Concatenating)

-concat:方法可以将一个stream拼接在另一个stream后面

1
2
3
4
5
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)

-flatten操作符用于包含streamstream,将stream中的值也就是stream,组合成一个新的stream

序列的连接

1
2
3
4
5
6
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 *flattenedSequence = [sequenceOfSequences flatten];

信号的合并

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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)

扁平化本身并不是很意思,但是理解其工作原理对于flattenMap操作符来说很重要

-flattenMap:操作符用于将流中的每一个值转换成一个新的流,然后再将所有返会的流扁平化成单个的流。换句话说,就是先执行-flatten操作再执行-map:操作。

-flattenMap:操作符可以用来拓展和编辑序列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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:^RACStream *(NSNumber * value) {
return @[value, value].rac_sequence;
}];

// Contains: 1_ 3_ 5_ 7_ 9_
RACSequence*edited = [numbers flattenMap:^RACStream *(NSString *value) {
if(value.intValue %2 == 0){
return [RACSequence empty];
} else {
return [RACSequence return:[value stringByAppendingString:@"_"]];
}
}];

也可以用来创建多个自动重组的信号

1
2
3
4
5
6
7
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;

[[letters flattenMap:^(NSString *letter) {
return [RACSignal return:[letter stringByAppendingString:letter]];
}] subscribeCompleted:^{
NSLog(@"All database entries saved successfully.");
}];

组合信号(Combining Signal)

连接操作符可以将多个信号组合成单个信号

序列化(Sequencing)

-then:操作符开始于一个原始信号,并等待其完成,然后只转发新信号的值。

1
2
3
4
5
6
RACSignal *letters = [@"A B C D E F G H I" componentsSeparatedByString:@" "].rac_sequence.signal;
RACSignal *sequenced = [[letters doNext:^(id x) {
NSLog(@"%@",x);
}] then:^RACSignal * {
return [@"1 2 3 4 5 6 7 8 9" componentsSeparatedByString:@" "].rac_sequence.signal;
}];

合并(Merging)

+merge:操作符将那些从多个信号到达的值,转发到单个信号流中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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)

+combineLatest:+combineLatest:reduce:方法将观察多个信号的变化,当其中一个信号发生变化时,就将所有信号的最新值发送出去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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"];

注意,组合信号只会在所有输入发送至少一个输入时发送其第一个值。在上面的示例中,@”A”从未转发,因为numbers尚未发送值。

切换

-switchToLatest操作符作用于“信号的信号”,并且总是转发从最新的信号的值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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"];

以上就是ReactiveCocoa最常用的操作符的介绍