一直以来,都很想学学ReactiveCocoa这个神奇的技术,但是最后都由于各种原因搁置了。这次终于也认真的研究一番,把自己学习心得整理出来留个记录。
主要内容:
一、了解函数响应式编程
二、ReactiveCocoa简介
三、ReactiveCocoa集成
四、理解什么是信号
五、从源码理解RAC的信号机制
六、本篇总结
一、了解函数响应式编程
函数式编程(Funcational Programming)
使用高阶函数编程,即函数可采用多种函数作为它们的参数和返回值。
响应式编程(Reactive Programming)
一种面向数据流和变化传播的编程范式
函数响应式编程(Funcational Reacitve Programming)
简称FRP,ReactiveCocoa就是一个典型的FRP框架,响应式的编程思想,函数式的代码形式。
二、ReactiveCocoa简介
ReactiveCocoa(简称RAC),Reactive表示响应式,Cocoa是苹果整个框架的简称,许多苹果框架都以Cocoa结尾。所以RAC是Github上为我们提供函数响应式编程方法的iOS开发框架。
iOS开发中,我们需要使用按钮点击、代理、通知等这些方法来处理响应事件。而RAC框架使用Category为很多基本的UIKit控件添加信号Signal,这样我们可以通过信号来监听数据流与变化传播,把将监听的代码与处理代码放在一起,从而方便我们管理。利用此特点结合MVVM架构,RAC也有十分显著的作用。
三、ReactiveCocoa集成
RAC.5.0相对于之前版本对于自身项目结构进行了较大调整,被拆分ReactiveCocoa、ReactiveSwift、ReactiveObjC、ReactiveObjCBridge四个库,我们需要根据不同的情况来集成,GitHub地址如下:
https://github.com/ReactiveCocoa/ReactiveCocoa
通常,我们都使用Cocoapods集成RAC,需要注意的是Podfile文件中必须使用user_framework!,然后,针对于不同的代码环境,有三种集成情况:
1.纯OC工程
ReactiveObjc库包含原RAC2的全部代码,在纯OC工程中使用
2.纯Swift工程
纯Swfit工程继续使用ReactiveCocoa,但RAC依赖ReactiveSwift,所以相当于引入两个库。
集成方法同上,只不过将ReactiveObjc换成ReactiveCocoa。
3.OC与Swift混编工程
混编工程需要同时引入ReactiveCocoa与ReactiveObjCBridge,但是ReactiveObjCBridge库依赖于ReactiveObjc库,所以相当于同时引入四个库了。示例如下:
四、ReactiveCocoa信号理解
我觉得学习RAC的第一个关口就是理解信号RACSignal了,什么是信号也许是困惑我们的第一个问题。
作为RAC中最为核心的一个类,信号可以理解为传递数据变化信息的工具,信号会在数据发生变化时发送事件流给它的订阅者,然后订阅者执行响应方法。信号本身不具备发送信号的能力,而是交给一个订阅者去发出。
首先上一段代码,演示信号的一个基本使用。
测试场景:我们要对一个用于输入用户名的UITextFiled进行检测,每次输入内容变化的时候都打出输入框的内容,使用RAC来实现此操作的关键代码如下:
没错的,不使用代理方法,也没有action的响应处理,我们仅仅使用了一行方法就实现了对文本框输入内容的实时打印。由此,RAC的实用性可见一斑。
五、ReactiveCocoa信号机制
我们会对上面的代码产生疑问,RAC是怎么做到上述代码功能的呢?而且我们常说的订阅者又在哪里呢?
其实RAC已经使用Category的形式为我们基本的UI控件创建了信号(如上例中的rac_textSignal),所以这里我们才可以很方便的实现信号订阅,而且订阅者在整个过程中也是对于我们隐藏的。 现在我们使用自定义信号的方法,从创建信号到订阅信号细致的了解一下这个过程。首先上一段创建信号的测试代码如下:
我们通过观察源码来理解整个过程:
1.创建信号
创建信号,我们需要使用RACSignal的类方法createSignal。该方法需要一个Block作为参数。查看源码,我们就会发现RACSignal最终是通过调用自己子类RACDynamicSignal的createSignal方法,将这个Block设置给了自己的didSubscribe属性的。
didSubscribe:这是创建信号时候需要传入的一个block,它的传入参数是订阅者subscriber,而返回值是需要是一个RACDisposable对象。创建信号后的didSubscrib是一个等待执行的block。
RACSubscriber:表示订阅者,创建信号时订阅者发送信号,这里的订阅者是一个协议而非一个类。信号需要订阅者帮助其发送数据。查看RACSubscriber的协议,我可以看到以下几个方法:
在创建一个信号的时候,订阅者使用sendNext发送信息。而且如果我们不再发送数据,最好在这里执行一次sendCompleted方法,这样的话,信号内部会自动调用对应的方法取消信号订阅。
RACDisposable:这个类用于取消订阅信号和清理资源,在信号出现错误或者信号完成的时候,信号会自动调起RACDisposable对象的block方法。在代码中我们也可以看到,创建RACDisposable对象是使用disposableWithBlock方法设置了一个block操作,执行block操作之后,信号就不再被订阅了。
总结:创建信号就是使用createSignal方法,创建一个信号,并为信号设置了一个didSubscribe属性(也就是一系列订阅者需要做的操作)。
2.订阅信号
进入订阅信号的源码我们看到如下代码:
在此方法中,我们可以看到订阅信号有两个过程:
过程1:使用subscribeNext的方法参数,创建出一个订阅者subscriber。
过程2:信号对象执行了订阅操作subscribe,方法中传入参数是刚创建的订阅者。
注:这也就解释了我们常提起却看不见的订阅者存在哪里的问题。真实开发中我们只关心订阅者需要发送的值就行了,而不需要关心其内部订阅的过程。
继续打开信号的subscribe方法,看到源码如下:
上面的代码中我们不难看出:除了对于订阅者和清理对象的再次封装外,最重要的就是创建信号时为信号设置Block(didSubscribe)被调用了,而且Block参数使用了我们创建的订阅者。
六、本篇总结
1.自创建信号会传入一个Block(didSubscribe),Block中遵循协议的订阅者会调用sendNext方法发送消息。而在订阅信号subscribeNext时,会在内部创建一个订阅者,并将其传递给原先赋值的didSubscribe,并执行这个Block。
2.但是我们应该注意:上述的分析只是其中信号机制的一种情况罢了。RAC对于UI组件信号的封装可能有所不同,比如之前我们看到的输入框信号,执行订阅信号subscribeNext时并不立即执行打印,而是监听到输入时打印。这其实是该信号使用了concat又做了一系列的操作。对于不同的信号我们只需要理解上述分析中提到几个关键属性,就可以结合源码很好的理解信号机制的使用了。
本篇的重点在于对RAC的基本介绍,是为了更好的理解信号机制,这仅相当于打开一个切入口来认识RAC。关于RAC更为详细用法可以参考下一篇:ReactiveCocoa函数响应式编程-应用篇。
领取专属 10元无门槛券
私享最新 技术干货