ReactiveCocoa(RAC)

1.ReactiveCocoa简介

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

2.ReactiveCocoa作用

在我们IOS开发中,经常会响应某些事件来处理某些业务逻辑,例如按钮的点击、上下拉刷新、网络请求、属性的变化(通过KVO)或者用户位置的变化(通过CoreLocation)。但是这些事件都用不同的方式来处理,比如action、delegate、KVO、callback等。

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

3.编程思想

在开发中我们也不能太依赖于某个框架,否则这个框架不更新了,导致项目后期没办法维护,比如之前Facebook提供的Three20框架,在当时也是神器,但后来不更新了,也就没什么人用了。因此我感觉学习一个框架,还是有必要了解它的编程思想。

先简单地介绍下目前我们已知的编程思想:

3.1、面向过程:处理事物以过程为核心,一步一步地实现。

3.2、面向对象:万物皆对象。

3.3、链式编程思想:是将多个操作(多行代码)通过点语法链接在一起成为一句代码,是代码可读性好。

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

       代表:masonry框架

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

       代表:KVO运用

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

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

       代表:ReactiveCocoa

4.ReactiveCocoa编程思想

ReactiveCocoa结合了几种编程风格:

   函数式编程(Functional Programming)

   响应式编程(Reactive Programming)

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

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

5.如何导入ReactiveCocoa框架

从上图这样导入RAC框架会发现,会报错的。

解决办法:加上 'use_frameworks!'

6.ReactiveCocoa常见类

学习框架首要之处:个人认为先要搞清楚框架中常用的类,在RAC中最核心的类RACSignal,搞定这个类就能用ReactiveCocoa开发了。

RACSignal:信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。

        信号类(RACSignal),只是表示数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。

        默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。

        如何订阅信号:调用信号RACSignal的subscribeNext就能订阅。

6.1.RACSignal的简单运用

    RACSignal使用步骤:

        1.创建信号:+ (RACSignal *)createSignal:(RACDisposable * (^)(id subscriber))didSubscribe

       2.发送信号:- (void)sendNext:(id)value (这步不一定有)

       3.订阅信号,才能激活信号:- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

RACSignal底层实现:

       1.创建信号,首先把didSubscribe保存到信号中,还不会触发

       2.当信号被订阅,也就是调用signal的subscribeNext:nextBlock

       2.subscribeNext内部会创建订阅者subscriber,并把nextBlock保存到subscriber中

       2.subscribeNext内部会调用signal的didSubscribe

       3.signal的didSubscribe中调用[subscriber sendNext:@1];

       3.1 sendNext底层其实就是执行了subscriber的nextBlock

我们来创建一个信号并发送

创建信号并发送

我们运行程序可以发现,控制台并没有打印NSLog里的字符串,也印证了,创建的信号默认是个冷信号,需要我们手动去激活变成热信号,只有当信号被订阅了,才会调用。

这里,我加上了订阅信号方法后,可以看到,控制台正常输出了想要的东西。 (注:信号内部发送数据,订阅方法就被调用;信号被订阅,信号内部就会被调用)

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

 RACDisposable:用于取消订阅者或者清理资源,当信号发送完毕或者发送错误的时候,就会自动释放它。一般使用的地方:不想监听某个信号时,可以通过它来主动取消订阅者号。

     1.需要在信号被订阅的时候,返回一个RACDisposable,才能取消信号。

      2.只要订阅信号,就会返回一个取消信号的对象,通过subscribeNext订阅,返回来RACDisposable对象,再通过dispose来主动取消信号

6.2.RACSubject

  信号提供者,自己可以充当信号,又能发送信号。rac中热信号的源头,继承于RACSignal

   RACSubject的使用步骤:

            1.创建信号 [RACSubject subject],跟RACSignal不一样,创建信号时没有block。

            2.订阅信号- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

            3.发送信号- (void)sendNext:(id)value

    RACSubject底层实现和RACSignal不一样:

             1.调用subscribeNext订阅信号,只是把订阅者保存起来,并且订阅者的nextBlock已经赋值。

             2.调用sendNext发送信号,遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock

    RACSubject的简单使用:

单个订阅
多个订阅

看log可以看出racsubject和racsignal有本质的区别,不关心历史值,错过就错过了,当发出第二个信号时,第一个订阅者收到的数据会相对于发生更新,所以不用去考虑历史值。

6.3.RACReplaySubject

    重复提供信号类,RACSubject的子类。

    使用场景:

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

               2.可以设置capcity数量来限制缓存的value的数量,即只缓存最新的几个值

   RACReplaySubject的简单使用:

只订阅1次
订阅2次

可以看到log打印,把之前的值都重新发送了一遍。

6.4.RACTuple和RACSequence

      RACTuple:元组类,类似NSArray,用来包装值

      RACSequence:RAC中的集合类,用来代替NSArray、NSDictionary,可以使用它来快速遍历数组和字典。

      使用场景:字典转模型

      RACSequence和RACTuple的简单使用

      主要有3个步骤 (numbers为数组)

            1.把数组转化成集合RACSequence    ->numbers.rac_sequence

            2.把集合RACSequence转化成RACSignal信号类      ->numbers.rac_sequence.signal

            3.订阅信号,激活信号,会自动把集合中的所有值遍历出来  ->subscribeNext

RACSequence

从log打印可以看到,遍历字典,有个RACTuple对象。那么可以通过RACTuple来处理,方便接下来直接取出key-value

RACTuple1
RACTuple宏

     字典转模型

     通过map(映射)将数组遍历的集合映射成一个新值(模型)

     步骤:

            1. map:映射的意思,目的:把原始值value映射成一个新值

            2.toArray:把集合转成数组

            底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并保存到数组中

字典转模型

6.5.RACCommand

     RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,可以很方便的监控事件的执行过程。

     使用场景:监听按钮点击,网络请求

     使用步骤:

          1.创建命令- (id)initWithSignalBlock:(RACSignal * (^)(InputType input))signalBlock;

          2.在signalBlock中,创建RACSignal,并作为signalBlock的返回值。

          3.执行命令- (RACSignal *)execute:(InputType)input;

      注意事项:

         1.signalBlock必须返回一个信号,不能传nil

         2.如果不想传递信号,直接创建空的信号[RACSignal empty]

         3.RACCommand中信号如果数据传递完,调用[subscriber sendCompeted]来保证发送信号完毕,不会有更多的信号发送。

         4.RACCommand需要被强引用,否则接受不到RACCommand中的信号,因此RACCommand的信号是延迟发送的。

从上图,可以看出,创建命令返回的是一个信号RACSignal,我们也知道信号可以发信号,所以,就有executionSignals信号源的概念

executionSignals 信号源,包含事件处理的所有的信号

定义:信号中发信号,也就是信号发出的数据也是信号。

信号发送信号

明白了信号发送信号的原理后,我们来看看executionSignals信号源如何去操作的。需要注意的是想要信号源接收到信号内容,需要将命令类强引用保持,保证命令类不被销毁。(在这里我就设置成全局)

RACCommand->RACSignals

6.6.RACMulticastConnection

       用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。

       使用注意:RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建

       使用步骤:

              1.创建信号+ (RACSignal *)createSignal:(RACDisposable * (^)(idsubscriber))didSubscribe;

              2.创建连接RACMulticastConnection *connect = [signal publish];

              3.订阅信号,注意:订阅的信号不是之前的信号,而是连接的信号[connection.signal subscribeNext:nextBlock]

            4.连接 [connect connect];

       底层原理:

            1.创建connect,通过publish(multicast:)创建sourceSignal(指向原信号),connect.signal ->RACSubject

            2.订阅connect,会调用RACSubject的subscribeNext,创建订阅者,并把订阅者保存起来,并不会去去执行,也就是说现在的connect.signal信号是冷信号。

            3.[connect connect]内部会订阅RACSignal(原始信号),并且订阅者就是RACSubject,调用了此方法才能将信号变成热信号。

            3.订阅原始信号,就会调用didSubscribe

            3.didSubscribe后,拿到订阅者调用sendNext,其实就是调用RACSubjec的sendNext

            4.sendNext后,就会遍历所有订阅者发送信号

       使用场景:

            如果一个信号发出请求,每次订阅一次都会发出请求,这样导致多次请求,就可以利用RACMulticastConnection来解决

       简单使用:

单独的订阅信号

如果不使用连接,可以发现,每次去订阅一次信号,就会去重复调NSLog(),这个也就是副作用。

加上连接Signal

可以看到,就不会去重复请求了。

7.ReactiveCocoa开发中常见用法

    1.代替代理:

              ran_signalForSelector:用于代替代理

     场景:在控制器设置一个view,view里放个按钮button,然后点击button,让控制器知道按钮被点了。

    拓展:RACDelegateProxy 代理类

           这个类通常用在多个对象,但各种在代理方法中处理的事件不同,或者说实现不同的需求(注:RACDelegateProxy可能会被无缘无故地crash,所以需要强引用保留住)

订阅信号返回block参数x就是当前操作的textField。这样写代理,符合了高内聚的思想,不必去到处找代理方法位置。

    2.代替KVO:

              rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变

       场景:点击屏幕,年龄age++,来监听age变化值

    3.监听事件:

              rac_signalForControlEvents:用于监听某个事件

    4.代替通知:

             rac_addObserverForName:用于监听某个通知

         场景:监听键盘弹出

    5.监听文本框文字改变:

            rac_textSignal:只要文本框发出改变就会发出这个信号

    6.处理当界面有多次请求时,需要都获取到数据时,才能显示界面:

           rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组),每一个signal(都至少sendNext过一次,就会去触发第一个selector参数的方法)。

        场景:一个页面里,有2个不同的请求(分别是热门商品的请求和最新商品的请求),只有当2个请求都请求完毕后才更新界面。

我们将最新商品发送信号方法senNext注释掉,会发现就不会去调更新UI方法了。

8.ReactiveCocoa常见的宏


总结:

   ReactiveCocoa框架是个重量级框架,学习起来确实比较费时间,学习一个框架,要去了解框架编程思想,比如Masonry框架,采取的是链式编程,ReactiveCocoa框架,采取的是函数响应式编程。框架固然方便了程序员,但不能太过于去依赖,如果有一天框架没更新了,写的代码就真的炸了,为此,我不推荐项目迭代时间较久的去使用第三方框架。

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

推荐阅读更多精彩内容

  • RAC使用测试Demo下载:github.com/FuWees/WPRACTestDemo 1.ReactiveC...
    FuWees阅读 6,349评论 3 10
  • 1.ReactiveCocoa简介 ReactiveCocoa(简称为RAC),是由Github开源的一个应用于i...
    清蘂翅膀的技术阅读 1,973评论 0 1
  • 1.ReactiveCocoa简介 ReactiveCocoa(简称为RAC),是由Github开源的一个应用于i...
    爱睡觉的魚阅读 1,141评论 0 1
  • 生活的雨,总是连泥带水的。没有高尚的轻松,也不总是必死的狼狈。你有你的理想,觉得换一方天地大有可为,他有他的顾虑,...
    思念potato阅读 154评论 0 1
  • 回到有你的地方,感觉你还在这里,心里似乎并没有想象中的那么悲伤,仅仅只是离开了一天,我都在想象着你突然出现给我的一...
    嘴角一抹冷笑阅读 154评论 0 0