美文网首页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