美文网首页ios专题iOS开发
ReactiveCocoa(简称RAC)基础篇

ReactiveCocoa(简称RAC)基础篇

作者: HeavenWong | 来源:发表于2018-04-24 16:35 被阅读8次

参考文章1袁峥
参考文章2
参考文章3
参考文章4

一、ReactiveCocoa简介

  • ReactiveCocoa是苹果iOS开发的开源库。其实里面十分灵活的运用Block,其最大的营养就是他的编程思想。

二、好用编程思想

  • 写过swift的就会发现,它调用方法的方式大都是通过.的方式,传参是通过.方法名(参数)的方式。说明苹果也在推荐使用的吧。

  • 优点:用OC的方式可能要写几行代码才能达到想要的效果。而链式编程+函数式编程,就是一行代码就能搞定,而且通熟易懂。代码高聚合,方便管理。

  • 链式编程思想:就是方法函数通过.的方式来调用

  • 函数式编程思想.方法名(函数(返回自身对象))

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

  • 链式编程+函数式编程的代表作:masnory

//  第三方库masnory简单使用
[btn mas_makeConstraints:^(MASConstraintMaker *make) {
        make.centerX.mas_equalTo(0);
        make.centerY.mas_equalTo(-150);
        make.width.mas_equalTo(88);
        make.height.mas_equalTo(50);
    }];

2.1 实学实用

  • 很简单的
  • 来一波大白话解释:
manager.add(2,2).sub(1).muilt(2).divide(3);

1. 在OC中,block可以用`方法名()`的方式来调用,调用Block的时候,blockName(params)。所以add(param)和sub(param)这两个方法,肯定返回值是一个Block,而且是带一个参数的Block。
2. 通过 `.` 调用方法其实就是调用getter方法,所以add和sub方法没有参数,只有一个返回类型为Block的值。
3. 为什么能一直点下去,因为Block也有返回值,而且返回值的类型就是当前对象的类型。
  • 通过实际案例开发的方式,能更快速的理解链式编程和函数式编程。接下来通过实例来加推理解。
2.2 使用链式编程+函数编程来写一个计算器功能
  • 创建计算器管理者CalculateManager,并声明实例方法和加减乘除方法。
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@class WMCalculateManager;

typedef WMCalculateManager *(^CalculateName)(CGFloat value);// 声明一个公开类型:带CGFloat类型的参数,返回WMCalculateManager类型,名字为CalculateName的block类型

@interface WMCalculateManager : NSObject

@property (nonatomic, copy) CalculateName calculateName;

// 参数(函数):带有本类对象的、无返回值类型 函数(block)
+ (CGFloat)makeCalculateManagerWith:(void(^)(WMCalculateManager *manager))managerBlock;

- (WMCalculateManager *(^)(CGFloat value1,CGFloat value2))add;
- (CalculateName)sub;
- (CalculateName)muilt;
- (CalculateName)divide;

@end
  • 执行上面声明的方法:
@interface WMCalculateManager ()

@property (nonatomic, assign) CGFloat result;

@end

@implementation WMCalculateManager
+ (CGFloat)makeCalculateManagerWith:(void (^)(WMCalculateManager *))managerBlock {
    // 创建自身实例化
    WMCalculateManager *manager = [[WMCalculateManager alloc] init];
    // 调用managerBlock,把 自身实例化对象 当参数
    managerBlock(manager);
    // 返回值
    return manager.result;;
}

- (WMCalculateManager *(^)(CGFloat value1, CGFloat value2))add {
    // 返回值 是 一个 带 自身返回值 和 参数 的函数
    return ^WMCalculateManager *(CGFloat value1, CGFloat value2){
        _result = value1 + value2 ;
        return self;
    };
}

- (CalculateName)sub {
    return ^WMCalculateManager *(CGFloat value1) {
        _result -= value1;
        return self;
    };
}

- (CalculateName)muilt {
    return ^WMCalculateManager *(CGFloat value1) {
        _result = _result * value1;
        return self;
    };
}

- (CalculateName)divide {
    return ^WMCalculateManager *(CGFloat valuea) {
        _result = _result / valuea;
        return self;
    };
}

@end
  • 使用上面创建的计算器
CGFloat result = [WMCalculateManager makeCalculateManagerWith:^(WMCalculateManager *manager) {
        manager.add(2,2).sub(1).muilt(2).divide(3);
    }];
    _resultLab.text = [NSString stringWithFormat:@"结果:%.1f",result];
    NSLog(@"result:%.1f",result);
// 输出结果:result:2.000000
  • 接下来就参照以上的模式应用到自己的项目中吧。可以用到很多地方。你会发现越来越喜欢使用Block回调的方式+链式。


三、ReactiveCocoa编程思想

  • 翻篇了
  • 那是不可能滴,这辈子是不可能滴。以上的编程思想理解之后,再往下看ReactiveCocoa辣是so easy,辣里不会点辣里。又犯二=_=!!
  • 开发中不可避免的使用牛人们开发好的框架,好用省时间,但不要太过依赖,否则人家不更新了就导致项目后期不好维护。对一个框架的学习,主要学习其编程思想。四面八方通罗马,达到同一个目的有很多种思路,看你走哪条路,别人怎么走的sa。

四、ReactiveCocoa使用

4.1. ReactiveCocoa导入

  • ReactiveCocoa5.0以后将RAC拆分为四个库:ReactiveCocoa、ReactiveSwift、ReactiveObjC、ReactiveObjCBridge。其中的ReactiveCocoa和ReactiveObjC,一个适用于您的纯Swift项目,另一个适用于纯OC项目。
  • 有多种方式导入。我是使用Cocoapods导入。我项目是纯OC,故导入ReactiveObjC
pod 'ReactiveObjC'

4.2 ReactiveCocoa常用的应用场景

  • 总结6个常用的应用场景:
    4.2.1 替代代理

  • rac_signalForSelector:用于替代代理
    4.2.2 替代KVO
  • rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变。
    4.2.3 替代通知
  • rac_addObserverForName:用于监听某个通知。
    4.2.4 监听事件
  • rac_signalForControlEvents:用于监听某个事件。
    4.2.5 监听文本框
  • rac_textSignal:只要文本框发出改变就会发出这个信号。
    4.2.6 处理当界面有多次请求时,需要都获取到数据时,才能展示界面(这个非常好用)
  • rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组),每一个signal都至少sendNext过一次,就会去触发第一个selector参数的方法。
  • 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。
  • 老规矩,用实例说话,推进理解↓

// 1.代替代理
    // 需求:自定义redView,监听红色view中按钮点击
    // 之前都是需要通过代理监听,给红色View添加一个代理属性,点击按钮的时候,通知代理做事情
    // rac_signalForSelector:把调用某个对象的方法的信息转换成信号,就要调用这个方法,就会发送信号。
    // 这里表示只要redV调用btnClick:,就会发出信号,订阅就好了。
    [[redV rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
        NSLog(@"点击红色按钮");
    }];

    // 2.KVO
    // 把监听redV的center属性改变转换成信号,只要值改变就会发送信号
    // observer:可以传入nil
    [[redV rac_valuesAndChangesForKeyPath:@"center" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];

    // 3.监听事件
    // 把按钮点击事件转换为信号,点击按钮,就会发送信号
    [ __weak typeof(self) weakSelf = self;// 注意:block中self要弱引用,避免强强引用无法释放。
    [[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
        // 点击按钮就会来到这里发送信号
        [weakSelf caculateName1:btn];
    }];

    // 4.代替通知
    // 把监听到的通知转换信号
    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
        NSLog(@"键盘弹出");
    }];

    // 5.监听文本框的文字改变
   [_textField.rac_textSignal subscribeNext:^(id x) {
       NSLog(@"文字改变了%@",x);
   }];
   
   // 6.处理多个请求,都返回结果的时候,统一做处理.
    RACSignal *request1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 发送请求1
        [subscriber sendNext:@"发送请求1"];
        return nil;
    }];
    
    RACSignal *request2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // 发送请求2
        [subscriber sendNext:@"发送请求2"];
        return nil;
    }];
    
    // 使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。
    [self rac_liftSelector:@selector(updateUIWithR1:r2:) withSignalsFromArray:@[request1,request2]];  
    
}
// 更新UI
- (void)updateUIWithR1:(id)data r2:(id)data1
{
    NSLog(@"更新UI%@  %@",data,data1);
}

4.3. ReactiveCocoa常用方法

RACSignal 信号类
  • RACSignal信号类,当有数据改变,信号内部收到数据就会交给内部一个订阅者去发信号传递数据。
  • 默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发
// RACSignal使用步骤:
    // 1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe
    // 2.订阅信号,才会激活信号. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
    // 3.发送信号 - (void)sendNext:(id)value
    
    
    // RACSignal底层实现:
    // 1.创建信号,首先把didSubscribe保存到信号中,还不会触发。
    // 2.当信号被订阅,也就是调用signal的subscribeNext:nextBlock
    // 2.2 subscribeNext内部会创建订阅者subscriber,并且把nextBlock保存到subscriber中。
    // 2.1 subscribeNext内部会调用siganl的didSubscribe
    // 3.siganl的didSubscribe中调用[subscriber sendNext:@1];
    // 3.1 sendNext底层其实就是执行subscriber的nextBlock
    
    // 1.创建信号 先保存信号代码,还不会被触发
     _signal1 = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
        // block调用时刻:每当有订阅者信号,就会调用block
        // 2. 发送信号
        [subscriber sendNext:@1];
        // 3.发送完成(如果不再发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号)
        [subscriber sendCompleted];
        return [RACDisposable disposableWithBlock:^{
            // block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
            
            // 执行完Block后,当前信号就不在被订阅了。
            DLog(@"发送完成或发送错误,总之信号被销毁");
        }];
    }];

 // 3.订阅信号,才会激活信号.
 [_signal1 subscribeNext:^(id  _Nullable x) {
        DLog(@"\n接收到数据:%@\n",x);
    }];

RACDisposable
  • 用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
  • 使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。
RACSubject
  • RACSubject:信号提供者,自己可以充当信号,又能发送信号。

4.4. ReactiveCocoa常见的宏

  • RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
  • RACObserve(self, name):监听某个对象的某个属性,返回的是信号。
  • @weakify(Obj)和@strongify(Obj),一般两个都是配套使用,在主头文件(ReactiveCocoa.h)中并没有导入,需要自己手动导入,RACEXTScope.h才可以使用。但是每次导入都非常麻烦,只需要在主头文件自己导入就好了。
  • RACTuplePack:把数据包装成RACTuple(元组类)
  • RACTupleUnpack:把RACTuple(元组类)解包成对应的数据。

相关文章

网友评论

    本文标题:ReactiveCocoa(简称RAC)基础篇

    本文链接:https://www.haomeiwen.com/subject/dnfjlftx.html