ReactiveCocoa(简称RAC)基础篇

参考文章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(元组类)解包成对应的数据。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容

  • 首先来了解一下函数式反应型编程Functional Reactive Programming 函数式反应型编程是两...
    金融程序员阅读 700评论 0 1
  • 一、龙树菩萨《宝鬘论》中的修持菩萨慈悲之道之人的七大美好品质: 1、布施是给予你所拥有的事物; 2、持戒是善待他人...
    闪光的种子阅读 472评论 1 1
  • @property是一个编译器指令 编译器只要看到@property,就知道我们生产某一个属性的getter/se...
    MarkTang阅读 766评论 2 1
  • 诚实的婚姻在树下 筑起屋子 一代人的居所,一代人的死后复生 迷途的光明都睡在瓦片上 一群白马,我期望 新娘背上挂满...
    鐵鉦阅读 279评论 5 10
  • 今天开始起梳理自己的生活,有感知的去生活去感悟。 1时间规划,任务规划,心无旁骛,专注 2早睡早起,关注身体的感受...
    rosemary123阅读 168评论 0 0