iOS 开发成长中心iOS 你不知道的新鲜事

ReactiveCocoa的一些特性细节

2018-11-10  本文已影响106人  iOS入门级攻城尸

本文基于objc版本的ReactiveCocoa v2.5
ps:看这篇文章之前强烈推荐大家用5分钟时间看一下我的上一篇文章毫无干货的带你理解什么是函数式编程


RACSequence

- (void)sequence{
    NSArray *strings = @[ @"A", @"B", @"C" ];
    RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) {
        NSLog(@"%@", str);
        return [str stringByAppendingString:@"_"];
    }];
    
    NSLog(@"1");
    NSString *concatA = sequence.head;//打印A
    NSLog(@"2");
    NSString *concatB = sequence.tail.head;//打印B
    NSLog(@"3");
    NSString *concatB2 = sequence.tail.head;//不打印,直接从缓存中取
    NSLog(@"4");
    RACSequence *derivedSequence = [sequence map:^(NSString *str) {
        NSLog(@"5");
        return [@"_" stringByAppendingString:str];
    }];
    NSLog(@"6");
    //tail.head的目的是访问B,但是A和B都已经缓存过了,所以仍然不会重复执行sequence的block。而是打印两次5
    NSString *concatB3 = derivedSequence.tail.head;
    NSLog(@"7");
}

输出结果:1、A、2、B、3、4、6、5、5、7


RACEagerSequence

- (void)eagerSequence{
    NSArray *strings = @[ @"A", @"B", @"C" ];
    RACSequence *sequence = [strings.rac_sequence map:^(NSString *str) {
        NSLog(@"%@", str);
        return [str stringByAppendingString:@"_"];
    }];
    
    RACEagerSequence *eagerSequence = [sequence eagerSequence];
    NSLog(eagerSequence.head);
    NSLog(eagerSequence.tail.head);
    NSLog(eagerSequence.head);
    NSLog(eagerSequence.tail.head);
} 

执行到[sequence eagerSequence];
输出:A、B、C。
之后输出:A_、B_、A_、B_。


RACSignal


subscribeNext

    __block int aNumber = 0;
    
    // 每增加一个subscription。createSignal的block机会被执行一次。
    RACSignal *aSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
        aNumber++;
        NSLog(@"block执行了%d次",aNumber);
        [subscriber sendNext:@(aNumber)];
        [subscriber sendCompleted];
        return nil;
    }];
    
    [aSignal subscribeNext:^(id x) {
        NSLog(@"subscriber one: %@", x);
    }];
    
    [aSignal subscribeNext:^(id x) {
        NSLog(@"subscriber two: %@", x);
    }];

输出:

2018-11-08 16:51:02.311068+0800 RACDemo[5714:1320947] block执行了1次
2018-11-08 16:51:02.311278+0800 RACDemo[5714:1320947] subscriber one: 1
2018-11-08 16:51:02.311440+0800 RACDemo[5714:1320947] block执行了2次
2018-11-08 16:51:02.311538+0800 RACDemo[5714:1320947] subscriber two: 2

rac_sequence.signal

这种情况并不常用,不过我们还是要分析一下。

RACSignal *letters = [@"A B C" componentsSeparatedByString:@" "].rac_sequence.signal;
NSLog(@"xxx");
[letters subscribeNext:^(NSString *x) {
    NSLog(@"1%@", x);
}];
NSLog(@"yyy");
[letters subscribeNext:^(NSString *x) {
    NSLog(@"2%@", x);
}];
NSLog(@"zzz");

输出:

2018-11-08 17:55:46.474730+0800 RACDemo[6136:1391178] xxx
2018-11-08 17:55:46.475301+0800 RACDemo[6136:1391178] yyy
2018-11-08 17:55:46.475459+0800 RACDemo[6136:1391178] zzz
2018-11-08 17:55:46.475561+0800 RACDemo[6136:1391225] 1A
2018-11-08 17:55:46.475887+0800 RACDemo[6136:1391225] 2A
2018-11-08 17:55:46.476103+0800 RACDemo[6136:1391225] 1B
2018-11-08 17:55:46.476308+0800 RACDemo[6136:1391225] 2B
2018-11-08 17:55:46.476468+0800 RACDemo[6136:1391225] 1C
2018-11-08 17:55:46.476763+0800 RACDemo[6136:1391225] 2C

当输出到zzz的时候,letters已经添加了两个subscriber。为什么第一次添加subscribeNext的时候不立即输出,而是等到所有subscribeNext添加结束后才输出呢?后面再开个源码分析的文章来单独说明。


RACMulticastConnection

__block NSInteger index = 0;
    RACSignal *networkRequest = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
        NSLog(@"inner signal %ld", ++index);
        [subscriber sendNext:@(index)];
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            
        }];
    }];
    
    RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]];
    /*!调用connect后signal中的block会立即执行,后面再调用subscribeNext则不会执行block了,副作用h只执行1次*/
    [connection connect];
    
    [connection.signal subscribeNext:^(NSNumber *index) {
        NSLog(@"subscriber one: %ld", index.integerValue);
    }];

    [connection.signal subscribeNext:^(NSNumber *index) {
        NSLog(@"subscriber two: %ld", index.integerValue);
    }];

输出:

2018-11-08 17:05:54.997850+0800 RACDemo[5852:1349307] inner signal 1
2018-11-08 17:05:54.998385+0800 RACDemo[5852:1349307] subscriber one: 1
2018-11-08 17:05:54.998526+0800 RACDemo[5852:1349307] subscriber two: 1

doNext & doCompleted

__block unsigned subscriptions = 0;
    NSLog(@"1");
    RACSignal *loggingSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
        NSLog(@"enter signal block");
        subscriptions++;
        //会触发doNext
        [subscriber sendNext:@(subscriptions)];
        //会触发doCompleted
        [subscriber sendCompleted];
        return nil;
    }];
    
    /*!会返回一个新信号
     */
    NSLog(@"2");
    loggingSignal = [loggingSignal doCompleted:^{
        NSLog(@"doCompleted %u", subscriptions);
    }];
    NSLog(@"3");
    loggingSignal = [loggingSignal doNext:^(NSNumber *x) {
        NSLog(@"doNext %ld",x.integerValue);
    }];
    NSLog(@"4");
    //会立即执行
    [loggingSignal subscribeNext:^(NSNumber *x) {
        NSLog(@"subscribeNext %ld",x.integerValue);
    }];
    NSLog(@"5");
    //会立即执行
    [loggingSignal subscribeCompleted:^{
        NSLog(@"subscribeCompleted %u", subscriptions);
    }];
    NSLog(@"6");

输出:

2018-11-10 12:03:08.946714+0800 RACDemo[1403:66623] 1
2018-11-10 12:03:08.949269+0800 RACDemo[1403:66623] 2
2018-11-10 12:03:08.949631+0800 RACDemo[1403:66623] 3
2018-11-10 12:03:08.949755+0800 RACDemo[1403:66623] 4
2018-11-10 12:03:08.950598+0800 RACDemo[1403:66623] enter signal block
2018-11-10 12:03:08.950693+0800 RACDemo[1403:66623] doNext 1
2018-11-10 12:03:08.950799+0800 RACDemo[1403:66623] subscribeNext 1
2018-11-10 12:03:08.950916+0800 RACDemo[1403:66623] doCompleted 1
2018-11-10 12:03:08.951015+0800 RACDemo[1403:66623] 5
2018-11-10 12:03:08.951110+0800 RACDemo[1403:66623] enter signal block
2018-11-10 12:03:08.951177+0800 RACDemo[1403:66623] doNext 2
2018-11-10 12:03:08.951253+0800 RACDemo[1403:66623] doCompleted 2
2018-11-10 12:03:08.951330+0800 RACDemo[1403:66623] subscribeCompleted 2
2018-11-10 12:03:08.951418+0800 RACDemo[1403:66623] 6

Transforming streams

map

RACSequence *letters = [@"A B C" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *mapped = [letters map:^(NSString *value) {
    return [value stringByAppendingString:value];
}];
[mapped.signal subscribeNext:^(id x) {
    NSLog(x);
}];

输出:AABBCC


filter

RACSequence *numbers = [@"1 2 3 4 5 6" componentsSeparatedByString:@" "].rac_sequence;
//只要偶数
RACSequence *filtered = [numbers filter:^ BOOL (NSString *value) {
    return (value.intValue % 2) == 0;
}];
[filtered.signal subscribeNext:^(id x) {
    NSLog(x);
}];

输出:246


concat

RACSequence *letters = [@"A B C" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *concatenated = [letters concat:numbers];
[concatenated.signal subscribeNext:^(id x) {
    NSLog(x);
}];

输出:ABC123


flatten

flatten for sequences

RACSequence *letters = [@"A B C" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *numbers = [@"1 2 3" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *sequenceOfSequences = @[ letters,numbers ].rac_sequence;
RACSequence *flattened = [sequenceOfSequences flatten];
[flattened.signal subscribeNext:^(id x) {
     NSLog(x);
}];

输出:ABC123

flatten for Signal

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];
flattened = [flattened doNext:^(NSString *x) {
    NSLog(@"doNext %@", x);
}];
[flattened subscribeNext:^(NSString *x) {
    NSLog(@"subscribeNext %@", x);
}];
[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];

输出:

2018-11-10 14:42:04.989738+0800 RACDemo[2038:140686] doNext A
2018-11-10 14:42:04.989942+0800 RACDemo[2038:140686] subscribeNext A
2018-11-10 14:42:04.990053+0800 RACDemo[2038:140686] doNext 1
2018-11-10 14:42:04.990127+0800 RACDemo[2038:140686] subscribeNext 1
2018-11-10 14:42:04.990221+0800 RACDemo[2038:140686] doNext B
2018-11-10 14:42:04.990315+0800 RACDemo[2038:140686] subscribeNext B
2018-11-10 14:42:04.990440+0800 RACDemo[2038:140686] doNext C
2018-11-10 14:42:04.990535+0800 RACDemo[2038:140686] subscribeNext C
2018-11-10 14:42:04.990634+0800 RACDemo[2038:140686] doNext 2
2018-11-10 14:42:04.990715+0800 RACDemo[2038:140686] subscribeNext 2

flattenMap

flattenMap for sequence

ps:先后顺序,先map可以修改stream流中的值,然后flatten可以保证生成一个一维的新stream

RACSequence *numbers = [@"1 2 3" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *extended = [[numbers
                              map:^id(id value) {
                                  return @[value,value].rac_sequence;
                              }] flatten];
[extended.signal subscribeNext:^(id x) {
    NSLog(x);
}];

上下两份代码等价。

RACSequence *numbers = [@"1 2 3" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *extended = [numbers flattenMap:^(NSString *num) {//这段代码等同于上面先map再flatten
        return @[ num, num ].rac_sequence;
}];
[extended.signal subscribeNext:^(id x) {
    NSLog(x);
}];

输出:112233
ps:这里不是输出112233这里没有字符串拼接,而是被拍扁了。

RACSequence *numbers = [@"1 2 3" componentsSeparatedByString:@" "].rac_sequence;
RACSequence *edited = [numbers flattenMap:^(NSString *num) {
    if (num.intValue % 2 == 0) {
        //这里必须有返回值但是又不能返回nil,empty不会成为新RACSequence中的项。
        return [RACSequence empty];
    } else {
        NSString *newNum = [num stringByAppendingString:@"_"];
        return [RACSequence return:newNum];
    }
}];
[edited.signal subscribeNext:^(id x) {
    NSLog(x);
}];

输出:1_3_

flattenMap for signal

/*!调用登录函数,成功后发送sendNext
 */
- (RACSignal *)signInSignal {
    return [RACSignal createSignal:^RACDisposable *(id subscriber){
        [self.signInService
            signInWithUsername:self.usernameTextField.text
            password:self.passwordTextField.text
            complete:^(BOOL success){
                [subscriber sendNext:@(success)];
                [subscriber sendCompleted];
            }];
            return nil;
    }];
}

/*!点击按钮后调用登录
 */
- (void)flattenMapForSignal{
    [[[self.signInButton
        rac_signalForControlEvents:UIControlEventTouchUpInside]
        flattenMap:^id(id x){
            return [self signInSignal];
        }]
        subscribeNext:^(id x){
            NSLog(@"登录结果: %@", x);
        }];
}

分析:
上例中当按钮点击后流中传递的是按钮点击的信号,通过flattenMap进行信号转换。


Combining signals

then

RACSignal *letters = [@"A B C" componentsSeparatedByString:@" "].rac_sequence.signal;
RACSignal *sequenced = [[letters
                             doNext:^(NSString *letter) {
                                 NSLog(@"doNext %@", letter);
                             }]
                            then:^{
                                NSLog(@"then");
                                return [@"1 2 3" componentsSeparatedByString:@" "].rac_sequence.signal;
                            }];
[sequenced subscribeNext:^(id x) {
    NSLog(@"subscribeNext %@",x);
}];

输出:

2018-11-10 15:24:03.646833+0800 RACDemo[2526:204404] doNext A
2018-11-10 15:24:03.647147+0800 RACDemo[2526:204404] doNext B
2018-11-10 15:24:03.647345+0800 RACDemo[2526:204404] doNext C
2018-11-10 15:24:03.647595+0800 RACDemo[2526:204404] then
2018-11-10 15:24:03.647890+0800 RACDemo[2526:204406] subscribeNext 1
2018-11-10 15:24:03.648029+0800 RACDemo[2526:204406] subscribeNext 2
2018-11-10 15:24:03.648158+0800 RACDemo[2526:204406] subscribeNext 3

merge

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *merged = [RACSignal merge:@[ letters, numbers ]];
[merged subscribeNext:^(NSString *x) {
    NSLog(@"%@", x);
}];
[letters sendNext:@"A"];
[numbers sendNext:@"1"];
[letters sendNext:@"B"];
[letters sendNext:@"C"];
[numbers sendNext:@"2"];

输出:A1BC2


combineLatest: reduce:

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSignal *combined = [RACSignal
                           combineLatest:@[ letters, numbers ]
                           reduce:^(NSString *letter, NSString *number) {
                               return [letter stringByAppendingString:number];
                           }];
[combined subscribeNext:^(id x) {
    NSLog(@"%@", x);
}];
[letters sendNext:@"A"];
[letters sendNext:@"B"];
[numbers sendNext:@"1"];
//不是盏结构 所以输出B2 而不是A2
[numbers sendNext:@"2"];
[letters sendNext:@"C"];
[numbers sendNext:@"3"];

输出:B1B2C2C3


switchToLatest

RACSubject *letters = [RACSubject subject];
RACSubject *numbers = [RACSubject subject];
RACSubject *signalOfSignals = [RACSubject subject];
RACSignal *switched = [signalOfSignals switchToLatest];
[switched subscribeNext:^(NSString *x) {
    NSLog(@"%@", x);
}];

[signalOfSignals sendNext:letters];
[letters sendNext:@"A"];
[letters sendNext:@"B"];
    
[signalOfSignals sendNext:numbers];
/*!因为先前往signalOfSignals发送了numbers所以最新的信号变成了numbers,这里再往letters发送值则subscribeNext不会触发*/
[letters sendNext:@"C"];
[numbers sendNext:@"1"];
    
[signalOfSignals sendNext:letters];
//这里又切换了最新的signal,所以再往numbers发送值则不会触发subscribeNext。
[numbers sendNext:@"2"];
[letters sendNext:@"D"];

输出:AB1D

上一篇下一篇

猜你喜欢

热点阅读