iOS - ReactiveCocoa

2020-04-02  本文已影响0人  GSNICE

1. ReactiveCocoa 简介

ReactiveCocoa(简称为RAC),是由Github开源的一个应用于 iOS 和 OS 开发的新框架,Cocoa 是苹果整套框架的简称,因此很多苹果框架喜欢以Cocoa结尾。

2. ReactiveCocoa 作用

3. ReactiveCocoa 编程思想

ReactiveCocoa 结合了几种编程风格:

所以,ReactiveCocoa 被描述为函数响应式编程(FRP)框架。

以后使用 RAC 解决问题,就不需要考虑调用顺序,直接考虑结果,把每一次操作都写成一系列嵌套的方法中,使代码高聚合,方便管理。

4. 编程思想

编程思想:通过开发需求,逐渐形成快速完成这些需求的思想。

4.1 面向过程:处理事情以过程为核心,一步一步的实现。
4.2 面向对象:万物皆对象。
4.3 链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)
4.4 响应式编程思想:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。
4.5 函数式编程思想:是把操作尽量写成一系列嵌套的函数或者方法调用。

5. 如何导入ReactiveCocoa框架

通常都会使用 CocoaPods(用于管理第三方框架的插件)帮助我们导入。

注意:

podfile 如果只描述 pod ‘ReactiveCocoa’,会导入不成功。

需要在 podfile 加上 use_frameworks!,重新 pod install 才能导入成功。

use_frameworks!
pod 'ReactiveObjC' 或者 pod 'ReactiveObjC', '~> 指定版本'
use_frameworks!
pod 'ReactiveCocoa' 或者pod 'ReactiveCocoa', '~> 指定版本'

6. ReactiveCocoa 常见类

学习框架首要之处:先要搞清楚框架中常用的类,在 RAC 中最核心的类 RACSiganl,会用这个类就能用 ReactiveCocoa 开发了。

  • RACSiganl:信号类,表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。
  • 信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。
  • 默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。
  • 如何订阅信号:调用信号 RACSignal 的 subscribeNext 就能订阅。
RACSiganl 简单使用:

RACSignal 使用步骤:

  1. 创建信号:

    • + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe
      
  2. 订阅信号,才会激活信号:

    • - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
      
  3. 发送信号:

    • - (void)sendNext:(id)value
      
#pragma mark - RACSignal
- (void)RACsignal {
    // 三步骤:创建信号,订阅信号,发送信号
    
    // 1.创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
        // 3.发送信号
        [subscriber sendNext:@1];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"默认信号发送完毕被取消");
        }];
    }];

    // 2.订阅信号
    RACDisposable *disposable = [signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    // 取消订阅
    [disposable dispose];
}

结果如下:


RACReplaySubject 与 RACSubject 区别:

RACReplaySubject 可以先发送信号,再订阅信号,RACSubject 就不可以。

RACSubject 简单使用:

RACSubject 使用步骤

  1. 创建信号 [RACSubject subject],跟 RACSiganl 不一样,创建信号时没有 block。

  2. 订阅信号:

    • - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
      
  3. 发送信号:

    • sendNext:(id)value
      
#pragma mark - RACSubject
- (void)RACSubject {
    // 信号提供者,自己可以充当信号,又能发送信号。
    
    // 创建信号
    RACSubject *subject = [RACSubject subject];
    
    // 订阅信号
    [subject subscribeNext:^(id x) {
        NSLog(@"订阅者一%@",x);
    }];
    [subject subscribeNext:^(id x) {
        NSLog(@"订阅者二%@",x);
    }];

    // 发送信号
    [subject sendNext:@"111"];
}

结果如下:


RACReplaySubject 的简单使用

RACReplaySubject 使用步骤:

  1. 创建信号 [RACSubject subject],跟 RACSiganl 不一样,创建信号时没有 block。

  2. 可以先订阅信号,也可以先发送信号。

    1. 订阅信号

      • (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
    2. 发送信号:

      • sendNext:(id)value
        
#pragma mark - RACReplaySubject
- (void)RACReplaySubject {
    // 创建信号
    RACReplaySubject *replaySubject = [RACReplaySubject subject];

    // 发送信号
    [replaySubject sendNext:@"222"];
    [replaySubject sendNext:@"333"];

    // 订阅信号
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"第一个订阅者%@",x);
    }];

   // 如果想一个信号被订阅,就重复播放之前所有值,需要先发送信号,在订阅信号。
    [replaySubject subscribeNext:^(id x) {
        NSLog(@"第二个订阅者%@",x);
    }];
}

结果如下:


  • RACTuple:元组类,类似 NSArray,用来包装值。
  • RACSequence:RAC 中的集合类,用于代替 NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
RACSequence 和 RACTuple 简单使用
- (void)ArrayTuple {
    NSArray *array = @[@1,@2,@3];
    [array.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}

结果如下:

- (void)DictionaryTuple {
    NSDictionary *dict = @{@"name":@"张三",@"age":@"20",@"sex":@"男"};
    [dict.rac_sequence.signal subscribeNext:^(id x) {
        // 解包元组,会把元组的值,按顺序给参数里面的变量赋值
        // RACTupleUnpack 这是个宏,后面会介绍
        RACTupleUnpack(NSString *key,NSString *value) = x;
        NSLog(@"%@---%@",key,value);
        /*
         相当于:
         NSString *key = x[0];
         NSString *value = x[1];
         */
    }];
}

结果如下:

使用场景:字典转模型

- (void)DictionaryToModel {
    // RAC高级写法:
    [AFNHelper get:kUrl parameter:nil success:^(id responseObject) {
        NSArray *array = responseObject;
        // map:映射的意思,目的:把原始值 value 映射成一个新值
        // array: 把集合转换成数组
        // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。
      NSArray *musicArray =[[array.rac_sequence map:^id(id value) {
          Music *music = [Music fetchMusicModelWithDict:value ];
            return music;
        }] array];
        NSLog(@"--------%@",musicArray);
    } faliure:^(id error) {
        NSLog(@"%@",error);
    }];
}

  • RACMulticastConnection:用于当一个信号被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的 block造成副作用,可以使用这个类处理。
  • 使用注意:RACMulticastConnection 通过 RACSignal 的 -publish 或者 -muticast: 方法创建。
RACMulticastConnection简单使用

需求:假设在一个信号中发送请求,每订阅一次都会发送请求,这样就会导致多次请求。
解决:使用 RACMulticastConnection 就能解决.

RACMulticastConnection使用步骤:

  1. 创建信号:

    • + (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe
      
  2. 创建连接:

    • RACMulticastConnection *connect = [signal publish];
      
  3. 订阅信号:

    • 注意:订阅的不再是之前的信号,而是连接的信号。

      • [connect.signal subscribeNext:nextBlock]
    • 连接

      • [connect connect]
  4. 发送信号

- (void)RACMulticastConnection {
    // 1.创建信号
    RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
       NSLog(@"请求一次");
       //5.发送信号
       [subscriber sendNext:@"2"];
       return nil;
    }];
    // 2.把信号转化为连接类
    RACMulticastConnection *connection = [signal publish];
    // 3.订阅连接类信号
    [connection.signal subscribeNext:^(id x) {
        NSLog(@"第一个订阅者%@",x);
    }];
    [connection.signal subscribeNext:^(id x) {
        NSLog(@"第二个订阅者%@",x);
    }];
    [connection.signal subscribeNext:^(id x) {
        NSLog(@"第三个订阅者%@",x);
    }];

    // 4.链接信号
    [connection connect];
}

结果如下:


  • RACCommand:RAC 中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。 >
  • 使用场景:监听按钮点击,网络请求。 >
RACCommand 简单使用
一、RACCommand使用步骤:
  1. 创建命令:

    • - (instancetype)initWithSignalBlock:(RACSignal * (^)(**id** input))signalBlock
      
  2. 在 signalBlock 中,创建 RACSignal,并且作为 signalBlock 的返回值

  3. 执行命令:

    • - (RACSignal *)execute:(**id**)input
      
二、RACCommand使用注意:
  1. signalBlock 必须要返回一个信号,不能传 nil.

  2. 如果不想要传递信号,直接创建空的信号:

    • [RACSignal empty];
      
  3. RACCommand 中信号如果数据传递完,必须调用 [subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。

三、RACCommand 设计思想:内部 signalBlock 为什么要返回一个信号,这个信号有什么用。

1.在 RAC 开发中,通常会把网络请求封装到 RACCommand,直接执行某个 RACCommand 就能发送请求。

2.当 RACCommand 内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过 signalBlock 返回的信号传递了。

四、如何拿到 RACCommand 中返回信号发出的数据。
  1. RACCommand 有个执行信号源 executionSignals,这个是 signal of signals (信号的信号),意思是信号发出的数据是信号,不是普通的类型。

  2. 订阅 executionSignals 就能拿到 RACCommand 中返回的信号,然后订阅 signalBlock 返回的信号,就能获取发出的值。

- (void)command {
    // 1.创建命令
    RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
        // 命令内部传递的参数
        NSLog(@"input===%@",input);
        // 2.返回一个信号,可以返回一个空信号 [RACSignal empty];
        return [RACSignal createSignal:^RACDisposable *(id subscriber) {
            NSLog(@"发送数据");
            // 发送信号
            [subscriber sendNext:@"22"];
            // 注意:数据传递完,最好调用 sendCompleted,这时命令才执行完毕。
            [subscriber sendCompleted];
            return nil;
        }];
    }];

    // 拿到返回信号方式二:
    // command.executionSignals 信号中的信号 switchToLatest 转化为信号
    [command.executionSignals.switchToLatest subscribeNext:^(id x) {
        NSLog(@"拿到信号的方式二%@",x);
    }];

    // 拿到返回信号方式一:
    RACSignal *signal =  [command execute:@"11"];

    [signal subscribeNext:^(id x) {
        NSLog(@"拿到信号的方式一%@",x);
    }];

    // 3.执行命令
    [command execute:@"11"];

    // 监听命令是否执行完毕
    [command.executing subscribeNext:^(id x) {
        if ([x boolValue] == YES) {
            NSLog(@"命令正在执行");
        }
        else {
            NSLog(@"命令完成/没有执行");
        }
    }];
}

结果如下:

7.ReactiveCocoa 常见宏

  • RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。

基本用法:

// 只要文本框文字改变,就会修改label的文字
RAC(self.labelView,text) = _textField.rac_textSignal;

结果如下:

  • RACObserve(self, name):监听某个对象的某个属性,返回的是信号。

基本用法:

[RACObserve(self.view, center) subscribeNext:^(id x) {
    NSLog(@"%@",x);
}];

结果如下:

  • RACTuplePack:把数据包装成 RACTuple(元组类)

基本用法:

// 把参数中的数据包装成元组
RACTuple *tuple = RACTuplePack(@10,@20);
NSLog(@"tuple = %@",tuple);

结果如下:

  • RACTupleUnpack:把 RACTuple(元组类)解包成对应的数据。

基本用法

// 把参数中的数据包装成元组
RACTuple *tuple = RACTuplePack(@"ZKQ",@25);
// 解包元组,会把元组的值,按顺序给参数里面的变量赋值
// name = @"ZKQ" age = @25
RACTupleUnpack(NSString *name,NSNumber *age) = tuple;
NSLog(@"name = %@,age = %@",name,age);

结果如下:

8.ReactiveCocoa 开发中常见用法

  • 代替代理:
    • rac_signalForSelector:用于代替代理。

需求:在使用数组下标为 3 的值前做额外的操作。

NSArray *array = @[@"1",@"2",@"3",@"4",@"5"];
    [[array rac_signalForSelector:@selector(objectAtIndex:)]
    subscribeNext:^(id x) {
        NSLog(@"做额外操作");
    }];
    NSLog(@"NSArray index_3:%@",[array objectAtIndex:3]);

结果如下:


  • 代替KVO :
    • rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变。
[[self.blueView rac_valuesAndChangesForKeyPath:@"backgroundColor" options:NSKeyValueObservingOptionNew observer:self] subscribeNext:^(RACTwoTuple<id,NSDictionary *> * _Nullable x) {
        NSLog(@"%@",x);
    }];
    [self.blueView setBackgroundColor:[UIColor redColor]];

结果如下:


  • 监听事件:
    • rac_signalForControlEvents:用于监听某个事件。

把按钮点击事件转换为信号,点击按钮,就会发送信号

// 监听事件
[[self.bt rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
    NSLog(@"点击了按扭");
}];

结果如下:


  • 监听文本框文字改变:
    • rac_textSignal:只要文本框发出改变就会发出这个信号。
      使用 @weakify(self); 和 @strongify(self); 解决循环引用问题。
// 监听文本框的文字改变
    @weakify(self);
    [self.textField.rac_textSignal subscribeNext:^(id x) {
        @strongify(self);
        NSLog(@"%@",x);
        self.labelView.text = x;
    }];

结果如下:

源码地址:ReactiveCocoaDemo

上一篇 下一篇

猜你喜欢

热点阅读