iOS:第三方库之 ReactiveCocoa

1. ReactiveCocoa 简介

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

2. ReactiveCocoa 作用

  • 在我们 iOS 开发过程中,当某些事件响应的时候,需要处理某些业务逻辑,这些事件都用不同的方式来处理。

  • 比如按钮的点击使用 action,使用 delegate,属性值改变使用 KVO,通知等系统提供的方式,都可以通过 RAC 处理。

  • ReactiveCocoa 为事件提供了很多处理方法,而且利用 RAC 处理事件很方便,可以把要处理的事情和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高内聚,低耦合的思想。

3. ReactiveCocoa 编程思想

ReactiveCocoa 结合了几种编程风格:

  • 函数式编程(Functional Programming)

  • 响应式编程(Reactive Programming)

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

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

4. 编程思想

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

4.1 面向过程:处理事情以过程为核心,一步一步的实现。

4.2 面向对象:万物皆对象。

4.3 链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)

  • 链式编程特点:方法的返回值是 block,block 必须有返回值(本身对象),block 参数(需要操作的值)

  • 代表:Masonry 框架。

4.4 响应式编程思想:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果,借用面向对象的一句话,万物皆是流。

  • 代表:KVO运用。

4.5 函数式编程思想:是把操作尽量写成一系列嵌套的函数或者方法调用。

  • 函数式编程本质:就是往方法中传入 Block,方法中嵌套 Block 调用,把代码聚合起来管理。

  • 函数式编程特点:每个方法必须有返回值(本身对象),把函数或者 Block 当做参数,Block 参数(需要操作的值)Block返回值(操作结果)。

  • 代表:ReactiveCocoa。

5. 如何导入ReactiveCocoa框架

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

注意:

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

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

  • Objective-C 版的 ReactiveCocoa (下面简称 RAC )集成方法
use_frameworks!
pod 'ReactiveObjC' 或者 pod 'ReactiveObjC', '~> 指定版本'
  • Swift 版的 ReactiveCocoa 集成方法
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];
}

结果如下:

 
66560-a89a50e8253c7f02.png
 

  • RACSubscriber:表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过 create 创建的信号,都有一个订阅者,帮助他发送数据。

  • RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。

    • 使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。
  • RACSubject:RACSubject 是信号提供者,自己可以充当信号,又能发送信号。

    • 使用场景:通常用来代替代理,有了它就不必要定义代理了。
  • RACReplaySubject:重复提供信号类,RACSubject 的子类。

RACReplaySubject 与 RACSubject 区别:

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

  • 使用场景一:如果一个信号每被订阅一次,就需要把之前的值重复发送一遍,使用重复提供信号类。

  • 使用场景二:可以设置 capacity 数量来限制缓存的 value 的数量,即只缓冲最新的几个值。

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"];
}

结果如下:

 
66560-a7e289af7ec70c48.png
 

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);
    }];
}

结果如下:

 
66560-9d60c0708c3c5141.png
 

  • RACTuple:元组类,类似 NSArray,用来包装值。
  • RACSequence:RAC 中的集合类,用于代替 NSArray,NSDictionary,可以使用它来快速遍历数组和字典。

RACSequence 和 RACTuple 简单使用

- (void)ArrayTuple {
    NSArray *array = @[@1,@2,@3];
    [array.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}

结果如下:

 
66560-5d88178a1eefeb99.png
 
- (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];
         */
    }];
}

结果如下:

 
66560-18a63969e3a55c6f.png
 

使用场景:字典转模型

- (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];
}

结果如下:

 
66560-861cd436a8e8d987.png
 

  • 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(@"命令完成/没有执行");
        }
    }];
}

结果如下:

 
66560-e7b67af252c18160.png
 

7.ReactiveCocoa 常见宏

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

基本用法:

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

结果如下:

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

基本用法:

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

结果如下:

 
66560-e6e201c05bd699e2.png
 
  • RACTuplePack:把数据包装成 RACTuple(元组类)

基本用法:

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

结果如下:

 
66560-150bbd2637bdc615.png
 
  • RACTupleUnpack:把 RACTuple(元组类)解包成对应的数据。

基本用法

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

结果如下:

 
66560-242d3e12419bb08d.png
 

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]);

结果如下:

 
66560-94fb9420239ebd73.png
 

  • 代替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]];

结果如下:

 
66560-3da913942f2e91e3.png
 

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

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

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

结果如下:

 
66560-bf2e6f81a0cbcb49.png
 

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

结果如下:

 
66560-92464b3977f18f16.png
 

源码地址:ReactiveCocoaDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值