对于RAC的一些常用类以及底层实现的总结!!!!

首先介绍一下什么是RAC

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

然后介绍一下RAC的作用,以及有哪些功能

在我们iOS开发过程中,当某些事件响应的时候,需要处理某些业务逻辑,这些事件都用不同的方式来处理。 比如按钮的点击使用action,ScrollView滚动使用delegate,属性值改变使用KVO等系统提供的方式。 其实这些事件,都可以通过RAC处理 ReactiveCocoa为事件提供了很多处理方法,而且利用RAC处理事件很方便,可以把要处理的事情,和监听的事情的代码放在一起,这样非常方便我们管理,就不需要跳到对应的方法里。非常符合我们开发中高聚合,低耦合的思想。

在这里说道RAC那就必须得说一下编程思想这个词了

编程思想的由来:在开发中我们会遇见各种各样的需求,经常会思考如何快速的完成这些需求,这样就会慢慢形成快速完成这些需求的思想。
先简单介绍下目前咱们已知的编程思想。
1、面向过程:处理事情以过程为核心,一步一步的实现。
2、面向对象:万物皆对象
3、链式编程思想:是将多个操作(多行代码)通过点号(.)链接在一起成为一句代码,使代码可读性好。a(1).b(2).c(3)

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

代表:masonry框架。

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

代表:KVO运用。

练习二:KVO底层实现。

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

函数式编程本质:就是往方法中传入Block,方法中嵌套Block调用,把代码聚合起来管理
函数式编程特点:每个方法必须有返回值(本身对象),把函数或者Block当做参数,block参数(需要操作的值)block返回值(操作结果)

代表:ReactiveCocoa。

练习三:用函数式编程实现,写一个加法计算器,并且加法计算器自带判断是否等于某个值.

#######4.ReactiveCocoa编程思想
ReactiveCocoa结合了几种编程风格

函数式编程(Functional Programming)

响应式编程(Reactive Programming)

所以,你可能听说过ReactiveCocoa被描述为函数响应式编程FRP)框架。

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

#######如何导入ReactiveCocoa框架
使用CocoaPods(用于管理第三方框架的插件)帮助我们导入。
具体步骤:PS:CocoaPods教程(http://code4app.com/article/cocoapods-install-usage

podfile如果只描述pod 'ReactiveCocoa', '~> 4.0.2-alpha-1',会导入不成功
报错信息:会提示我们说Swift什么什么玩意的。然后我们需要打开一句话,use_frameworks因为RAC需要用到Swift的一些东西

下面会具体介绍一下RAC中的一些类的基本使用

1.首先介绍RACSignal这个类 使用流程是 1创建信号,2订阅者订阅信号,3发送信号
RACSignal信号类!当数据改变时,信号内部接收到数据,就会马上发出数据,但是RACSignal本身是不具有发送消息的功能,而是交给内部一个叫做信号订阅者发出的!(冷信号
创建完以后必须要去订阅信号!只有订阅者把信号订阅以后冷信号才会变热信号!
代码实现

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // didSubscribe调用:只要一个信号被订阅就会调用
        // didSubscribe作用:发送数据
        NSLog(@"信号被订阅");
        // 3.发送数据
        [subscriber sendNext:@1];
        
        return nil;
    }];
  // 2.订阅信号(热信号)
    [signal subscribeNext:^(id x) {
        
        // nextBlock调用:只要订阅者发送数据就会调用
        // nextBlock作用:处理数据,展示到UI上面
        
        // x:信号发送的内容
        NSLog(@"%@",x);
    }];
   
    // 只要订阅者调用sendNext,就会执行nextBlock
    // 只要订阅RACDynamicSignal,就会执行didSubscribe
    // 前提条件是RACDynamicSignal,不同类型信号的订阅,处理订阅的事情不一样

下面介绍一下RACSignal的底层实现原理

第一步

RACSignal在创建的时候会带有一个Block这个Block什么是不被调用呢!看下一步!

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // didSubscribe调用:只要一个信号被订阅就会调用
        // didSubscribe作用:发送数据
        NSLog(@"信号被订阅");
#第三步在这里
        // 3.发送数据
        [subscriber sendNext:@1];
       发送数据的底层都帮我们做了什么呢?
        看了源代码发现,在我们发送这个信号的时候RAC内部会调用订阅者去发送数据!
        return nil;
    }];

然后我们进行下一步,去订阅刚刚创建的这个信号!

代码如下:
[signal subscribeNext:^(id x) {

    // nextBlock调用:只要订阅者发送数据就会调用
    // nextBlock作用:处理数据,展示到UI上面
    // x:信号发送的内容
    NSLog(@"%@",x);
}];

#######但是在订阅的工程中我们并没有去创建订阅者,我们只是调用了一下这个subscribeNext方法,这个方法内部帮我们做了很多事!比如说,在我们调用这个方法的时候,RAC内部会帮我们创建一个订阅者然后去订阅我们上一步创建的冷信号,在订阅后,冷信号随之变成热信号!

最后一步,看第一步中Block内!!!

上面是RACSignal的基本使用方法!

上面我们大致说来说RACSignal这个类怎么使用!
但是在RACSignal第一步中的Block室友返回值的可是当时我是直接返回了一个nil;现在说一下具体返回一个什么玩意!
首先我们还需要看一下RACSignal的创建方法

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // didSubscribe调用:只要一个信号被订阅就会调用
        // didSubscribe作用:发送数据
        NSLog(@"信号被订阅");
        // 3.发送数据
        [subscriber sendNext:@1];

        return nil;
    }];

经过观察我们不难发现RACSignal这个类后面的Block返回值是RACDisposable类型的返回值,那么我们就需要返回一个这样类型的返回值!
但是需要注意的是:就算我们不去释放/销毁信号,那么信号也会知道销毁!
我们试着自己去释放这个信号:代码如下

RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
        // didSubscribe调用:只要一个信号被订阅就会调用
        // didSubscribe作用:发送数据
        NSLog(@"信号被订阅");
        // 3.发送数据
        [subscriber sendNext:@1];

        return [[RACDisposable disposableWithBlock:^{

            // block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。

            // 执行完Block后,当前信号就不在被订阅了。

            NSLog(@"信号被销毁");

        }];];
    }];

#######通过RACDisposable 调用disposableWithBlock进行信号的销毁!

下面说一下一个新的类!RACSubject这个类,以及具体的实现过程

RACSubject :信号提供者:通常会替换代理!
特点 :既可以创建信号,又可以发送信号!
具体使用如下!
第一步:创建信号!

// 1.创建信号
    RACSubject *subject = [RACSubject subject];//直接通过类方法创建信号!

第二部:订阅信号!

// 不同信号订阅的方式不一样
    // RACSubject处理订阅:仅仅是保存订阅者
    [subject subscribeNext:^(id x) {
        NSLog(@"订阅者一接收到数据:%@",x);
    }];

第三部:发送信号!

 // 3.发送数据
    [subject sendNext:@1];// 这里是自己发送数据

具体的实现过程:

第一步中创建信号的时候:RACSubject会在内部创建一个可变数组,用来接收订阅者,当订阅者订阅信号的时候回把订阅者放到这个数组中!

第二部中当订阅者去订阅信号的时候会把创建的订阅者存放到第一步创建的数组中!

第三部中当subject发送数据的时候,会遍历第一步中的数组,然后取出所有的订阅者然后去发送数据!
这就是RACSubject这个类的基本使用!下面会具体介绍这个类如何作为代理去进行使用!

下面要介绍的是RACSubject的一个子类,RACReplaySubject这个类(重复提供信号类)用法基本和上面差不离!o

第一步:创建信号!

    RACReplaySubject *subject = [RACReplaySubject subject];

第二部:订阅信号!

    [subject subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];

第三部:发送信号!

    [subject sendNext:@1];

具体的实现过程:(跟它的父类相比,这个类比父类更牛13);

第一步中创建信号的时候:RACSubject会在内部创建一个可变数组,用来接收订阅者,当订阅者订阅信号的时候回把订阅者放到这个数组中!(和父类没什么两样)

第二部中当订阅者去订阅信号的时候会把要发送的值存放到第一步创建的数组中!,并且在这个一步就开始遍历数组中所有的值!

第三部中当subject发送数据的时候,会遍历第一步中的数组,然后取出所有的订阅者然后去发送数据!

这个和它父类唯一一个不一样的地方在于 这个类可以在信号被订阅之前发送数据,儿RACSubject 却不行,这就是区别!!

下面来说一下这个RACSubject如何替换代理!

拿一个简答的例子来说吧!
我们自己自定义了一个View,View上添加了一个Button,然后我们需要点击按钮通知控制器去做一些事情!
如果是原本的OC方法,我么需要写一整套的代理协议,如果使用RACSubject这个类的话,我们只需要在Button的点击方法中创建一个信号,然后在控制器中去订阅这个信号就OK了,就是这个简单!
具体代码如下

    // 需求:
    // 1.给当前控制器添加一个按钮,modal到另一个控制器界面
    // 2.另一个控制器view中有个按钮,点击按钮,通知当前控制器

步骤一:在第二个控制器.h,添加一个RACSubject代替代理。
@interface TwoViewController : UIViewController

@property (nonatomic, strong) RACSubject *delegateSignal;

@end

步骤二:监听第二个控制器按钮点击
@implementation TwoViewController
- (IBAction)notice:(id)sender {
    // 通知第一个控制器,告诉它,按钮被点了

     // 通知代理
     // 判断代理信号是否有值
    if (self.delegateSignal) {
        // 有值,才需要通知
        [self.delegateSignal sendNext:nil];
    }
}
@end

步骤三:在第一个控制器中,监听跳转按钮,给第二个控制器的代理信号赋值,并且监听.
@implementation OneViewController
- (IBAction)btnClick:(id)sender {

    // 创建第二个控制器
    TwoViewController *twoVc = [[TwoViewController alloc] init];

    // 设置代理信号
    twoVc.delegateSignal = [RACSubject subject];

    // 订阅代理信号
    [twoVc.delegateSignal subscribeNext:^(id x) {

        NSLog(@"点击了通知按钮");
    }];

    // 跳转到第二个控制器
    [self presentViewController:twoVc animated:YES completion:nil];

}
@end

下面介绍一下RAC中的集合类!

首先需要了解的是RACTuple这个类!这个叫做元组,但不是Swift中的元组,因为Swift中的元组可以存放基本数据类型,而RACTuple``不可以存放基本数据类型只能存放OC对象如果要存放基本数据类型需要进行包装
下面主要介绍的是如何对RACTuple进行遍历(数组)

废话不多说,上代码

主要思想就是,把数组转换为一个信号,然后去订阅这个信号,去执行一些操作!

RACSequence :就是RAC中的集合了,相当于OC中的数组和字典!,这是个信号类
     //创建一个数组
    NSArray *arr = @[@"213",@"321",@1];
 // 转换成RAC集合
    //    RACSequence *sequence = arr.rac_sequence;
     // 订阅集合信号,内部会自动遍历所有的元素发出来
    //    [signal subscribeNext:^(id x) {
    //        NSLog(@"%@",x);
    //    }];
  // 上面是具体的方法。
下面是直接通过链式编程直接就可以出来
[arr.rac_sequence.signal subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
下面是字典的遍历

直接上代码

这里需要解释一下:RACTupleUnpack 的含义,这是一个宏,定义的非常深,确实是没有看明白,但是也不妨碍我们去使用, RACTupleUnpack:用来解析元组,宏里面的参数,传需要解析出来的变量名,= 右边,放需要解析的元组

 // 字典
    NSDictionary *dict = @{@"account":@"aaa",@"name":@"xmg",@"age":@18};
    
    // 转换成集合
    [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
        //       NSString *key = x[0];
        //        NSString *value = x[1];
        //        NSLog(@"%@ %@",key,value);
        
        // RACTupleUnpack:用来解析元组
        // 宏里面的参数,传需要解析出来的变量名
        // = 右边,放需要解析的元组
        RACTupleUnpack(NSString *key,NSString *value) = x;
        
        NSLog(@"%@ %@",key,value);
    }];

上面的都是铺垫,下面介绍一个比较重要的,也是我们比较关心的———字典转模型,通过刚刚的方法去进行字典转模型

下面把一些主要的代码写一下!

.h中的代码
首先我们需要,一个Plist文件或者是JSON文件
首先我们创建一个模型,创建两个属性 
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *icon;
//然后来个类方法!
+ (instancetype)flagWithDict:(NSDictionary *)dict;

.m中的代码
//通过KVC进行赋值
+ (instancetype)flagWithDict:(NSDictionary *)dict
{
    Flag *flag = [[self alloc] init];
    
    [flag setValuesForKeysWithDictionary:dict];
    
    return flag;
}
下面是Controller中的代码
//下面以Plist文件为准
    // 解析plist文件
    NSString *filePath = [[NSBundle mainBundle]              pathForResource:@"flags.plist" ofType:nil];
    NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];     NSMutableArray *arr = [NSMutableArray array];
    [dictArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
    Flag *flag = [Flag flagWithDict:x];
    [arr addObject:flag];
  }];//上面是我们预想的方法,虽然很方便,但是还是不够牛13;
下面介绍一种比较叼的方法
   // 高级用法
    // 会把集合中所有元素都映射成一个新的对象
   NSArray *arr = [[dictArr.rac_sequence map:^id(NSDictionary *value) {
        // value:集合中元素
        // id:返回对象就是映射的值
        return [Flag flagWithDict:value];
    }] array];
    
    NSLog(@"%@",arr);
  //面试的时候可以深深的装个13.

下面我们接着介绍,RAC中的一些常用的方法!

RAC中另一种方法去代替delegate

代码如下:

详解:这个方法是用来监听某个方法实发触发的!!!

2.代替代理

 // 1.代替代理:1.RACSubject 2.rac_signalForSelector
    // 只要传值,就必须使用RACSubject
    [[_redView rac_signalForSelector:@selector(btnClick:)] subscribeNext:^(id x) {
        NSLog(@"控制器知道按钮被点击");
    }];
    // RAC:
    // 把控制器调用didReceiveMemoryWarning转换成信号
    // rac_signalForSelector:监听某对象有没有调用某方法
    //    [[self rac_signalForSelector:@selector(didReceiveMemoryWarning)] subscribeNext:^(id x) {
    //        NSLog(@"控制器调用didReceiveMemoryWarning");
    //    }];
2.代替KVO
  
去监测一个View的Frame是否改变
[_redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
        //
        
    }];
//如果我们要在一个Controller中监听多个值得改变的情况下我们可以直接日如下写!
 [[_redView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id x) {
      // x:修改的值
        NSLog(@"%@",x);
    }];
    
    [_redView rac_observeKeyPath:@"bounds" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
        //
        
    }];

3.监听事件

    [[_btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
        NSLog(@"按钮点击了");
    }];

4.代替通知

    [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
        
        NSLog(@"%@",x);
    }];

5.监听文本框

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

推荐阅读更多精彩内容