2018-10-12

[TOC]

简介

函数响应式编程(Functional Reactive Programming:FRP),ReactiveCocoa 版本 2.5

冷热信号

  • 冷信号

只有当你订阅的时候,它才会发布消息,

一对一,当有不同的订阅者,消息是重新完整发送。

  • 热信号

尽管你并没有订阅事件,但是它会时刻推送,类似“直播”,错过了就不再处理。

可以有多个订阅者,是一对多

RACSignalRACSubject 的区别

  • RACSignal是冷信号

  • RACSubject是热信号

如下图

  • signal
image.png
  • subject
image.png
  • replaySubject
image.png

Subject可以附加行为,例如RACReplaySubject具备为未来订阅者缓冲事件的能力。(这一点与冷信号类似,即使是在数据发送之后才订阅的,依然会收到全部消息)

冷信号示例:延时订阅,依然能收到所有信号数据


- (void)test1 {

    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {

        [subscriber sendNext:@1];

        [subscriber sendNext:@2];

        [subscriber sendNext:@3];

        [subscriber sendCompleted];

        return nil;

    }];

    NSLog(@"Signal was created.");

    [[RACScheduler mainThreadScheduler] afterDelay:0.1 schedule:^{

        [signal subscribeNext:^(id x) {

            NSLog(@"Subscriber1 recveive: %@", x);

        }];

    }];

    [[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{

        [signal subscribeNext:^(id x) {

            NSLog(@"Subscriber2 recveive: %@", x);

        }];

    }];

}

image.png

热信号示例:错过了订阅时机,就收不到信号数据,类似于直播

冷信号会收到全部的数据,即使是在数据发送之后才订阅的


- (void)test2 {

    RACMulticastConnection *connection = [[RACSignal createSignal:^RACDisposable *(id subscriber) {

        [[RACScheduler mainThreadScheduler] afterDelay:1 schedule:^{

            [subscriber sendNext:@1];

        }];

        [[RACScheduler mainThreadScheduler] afterDelay:2 schedule:^{

            [subscriber sendNext:@2];

        }];

        [[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{

            [subscriber sendNext:@3];

            [subscriber sendCompleted];

        }];

        return nil;

    }] publish];

    [connection connect];

    NSLog(@"Signal was created.");

    [[RACScheduler mainThreadScheduler] afterDelay:1.1 schedule:^{

        [connection.signal subscribeNext:^(id x) {

            NSLog(@"Subscriber1 recveive: %@", x);

        }];

    }];

    [[RACScheduler mainThreadScheduler] afterDelay:2.1 schedule:^{

        [connection.signal subscribeNext:^(id x) {

            NSLog(@"Subscriber2 recveive: %@", x);

        }];

    }];

}

image.png

SideEffect示例:多次订阅导致信号block多次执行


- (void)test3 {

    // 多次订阅会多次执行

    RACSignal *requestSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {

        NSLog(@"开始请求网络数据");

        [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{

            [subscriber sendNext:@"1"];

        }];

        return nil;

    }];

    // 【请求数据次数 +1】

    [requestSignal subscribeNext:^(id x) {

        NSLog(@"订阅者1");

    }];

    // 【请求数据次数 +1】

    [requestSignal subscribeNext:^(NSArray *x) {

        NSLog(@"订阅者2");

    }];

    // 将信号转换为内容为2的信号

    RACSignal *signal1 = [requestSignal flattenMap:^RACStream *(id value) {

        return [RACSignal return:@"2"];

    }];

    // 将signal1信号所有错误信息转换为字符串@"Error"

    [signal1 catchTo:[RACSignal return:@"Error"]];

    // 在没有获取值之前以字符串@"Loading..."占位

    [signal1 startWith:@"Loading..."];

    // 将信号进行绑定

    // 【请求数据次数 +1】

    RAC(self.acountField, text) = signal1;

    // 订阅多个信号的任何错误,并且弹出UIAlertView

    // 【请求数据次数 +2】

    [[RACSignal merge:@[requestSignal, signal1]] subscribeError:^(NSError *error) {

        NSLog(@"发生错误");

    }];

}

image.png

解决方式一:使用 RACMulticastConnection把冷信号转化为热信号


- (void)test4 {

    RACSignal *requestSignal = [RACSignal createSignal:^RACDisposable *(id subscriber) {

        NSLog(@"开始请求网络数据");

        [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{

            [subscriber sendNext:@"1"];

            [subscriber sendCompleted];

        }];

        return nil;

    }];

    RACMulticastConnection *connection = [requestSignal multicast:[RACSubject subject]];

//    RACMulticastConnection *connection = [requestSignal multicast:[RACReplaySubject subject]];

    [connection connect];

    [connection.signal subscribeNext:^(id x) {

        NSLog(@"订阅者1:%@", x);

    }];

    [connection.signal subscribeNext:^(NSArray *x) {

        NSLog(@"订阅者2:%@", x);

    }];

    [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{

        [connection.signal subscribeNext:^(NSArray *x) {

            NSLog(@"订阅者3:%@", x);

        }];

    }];

}

使用RACSubject


RACMulticastConnection *connection = [requestSignal multicast:[RACSubject subject]];

image.png

使用RACReplaySubject


RACMulticastConnection *connection = [requestSignal multicast:[RACReplaySubject subject]];

image.png

解决方式二:使用 replayLazily把冷信号转化为热信号


- (void)test5 {

    RACSignal *requestSignal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {

        NSLog(@"开始请求网络数据");

        [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{

            [subscriber sendNext:@"1"];

            [subscriber sendCompleted];

        }];

        return nil;

    }] replayLazily]; // modify here!!

    [requestSignal subscribeNext:^(id x) {

        NSLog(@"订阅者1:%@", x);

    }];

    [requestSignal subscribeNext:^(NSArray *x) {

        NSLog(@"订阅者2:%@", x);

    }];

    [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{

        [requestSignal subscribeNext:^(NSArray *x) {

            NSLog(@"订阅者3:%@", x);

        }];

    }];

}

image.png

使用RACCommand把冷信号转化为热信号


- (void)test6 {

    RACCommand *requestCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {

        return [RACSignal createSignal:^RACDisposable *(id subscriber) {

            NSLog(@"开始请求网络数据");

            [RACScheduler.mainThreadScheduler afterDelay:1 schedule:^{

                [subscriber sendNext:@"1"];

                [subscriber sendCompleted];

            }];

            return nil;

        }];

    }];

    RACSignal *requestSignal = [requestCommand execute:nil];

    [requestSignal subscribeNext:^(NSArray *x) {

        NSLog(@"订阅者1:%@", x);

    }];

    [requestSignal subscribeNext:^(NSArray *x) {

        NSLog(@"订阅者2:%@", x);

    }];

    [RACScheduler.mainThreadScheduler afterDelay:2 schedule:^{

        [requestSignal subscribeNext:^(NSArray *x) {

            NSLog(@"订阅者3:%@", x);

        }];

    }];

}

image.png

总结

RACMulticastConnectionRACSubject结合使用时是直播的热信号

RACCommandreplayLazilyRACReplaySubject都是类似于冷信号的情况,不管何时订阅,都会收到所有数据

ReactiveCocoa中潜在的内存泄漏与解决方案

  • RACObserve中潜在使用了self,要注意循环引用

  • RACSubject中如果没有调用sendCompleted,调用map等操作将造成内存泄漏(循环引用)。RACSignal不会有这个问题

代码参见https://github.com/action456789/ReactiveCocoaDemo

参考:http://tech.meituan.com/tag/ReactiveCocoa

你可能感兴趣的:(2018-10-12)