前言:
笔者接触RAC框架已经一年多的时间了,从2014年底开始接触到RAC,当时总觉得怪怪的,或者是因为经验不足吧,在学习以及使用过程中总是会闹点什么笑话。而说到RAC框架,不得不说起MVVM设计模式,本文中的内容主要是对RAC基于UI方面的封装的个人使用过程中的一些心得体会,好了,闲话不多说,开始干货吧~!
ReactiveCocoa简介
首先,关于RAC,相信很多对于iOS开发有过经验的开发者应该是不陌生的,ReactiveCocoa(简称为RAC
),是由Github开源的一个应用于iOS和OS开发的新框架。RAC框架通过对传统的KVO、KVC、Delegate、Block、NSNotification等一系列iOS/OS中关键性的设计模式做出了一个统一,形成了自己独特的响应式编程思维,通过Hot
和
SignalCool Signal
的抽象性概念,以类block的方法简化了我们的代码量。
ReactiveCocoa构建介绍
RAC的基本构建如下:
Core文件夹中包含了对OC里我们常用的类别的Category的拓展,以及RAC最重要也是最为关键的几个类:RACArraySequence、RACBacktrace、RACBehaviorSubject、RACBlockTrampoline、RACChannel、RACCommand、RACCompoundDisposable、RACDelegateProxy、RACDisposable、RACDynamicSequence、RACScheduler、RACReturnSignal、RACKVOProxy、RACStream、RACSubject、RACTuple、RACSignal等一系列以RAC为开头的类,大家如果有兴趣可以自己去下载一份RAC的源码,看看这些关键类的实现。
No-arc文件夹这是对于Runtime的一个方法封装,关于Runtime,笔者这里就不多说,相信大家哪怕没有正统看过或者用过也应该是听过Runtime的大名,RAC这里的Runtime笔者的理解是对这个Category的一个封装,该Category则是RAC方法的信号封装,有兴趣的读者可以去看看。
SupportFiles文件夹中存放的是跟cocoapods相关的文件,由于笔者使用的是cocoapods进行RAC框架的集成,而非下载源码后过通过自己的过滤来的源码库,关于cocoapods,这里不多做介绍。
UI文件夹中存放的是RAC关于UIKit中常用的控件的Category拓展,这个拓展是基于RAC对传统模式下的封装进行的,本文中,笔者将对这部分的干货进行个人使用过程中的心得进行介绍。
ReactiveCocoa – UI Category介绍
首先,我们先来看下RAC关于UIKit中到底做了些什么?如图:
以上就是RAC框架中对UIKit的一些拓展,那么,这些拓展是干什么用的呢?以UIAlertView为例。UIAlertView相信大家都不陌生,这个是系统下iOS的默认弹框,在项目中我们经常会去使用到它,常规的使用如下:
UIAlertView *alertView = [[UIAlertViewalloc]
initWithTitle:@"title"message:@"message"delegate:selfcancelButtonTitle:@"cancelButtonTitle"otherButtonTitles:@"otherButtonTitles",nil];
alertView.tag =
888;
[alertView show];
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
if (alertView.tag ==888 && buttonIndex ==
0) {
//对cancel的处理。。。。。
}
}
那么RAC中对UIAlertView做了怎么样的封装呢?让我们来看看它的Category吧!
RAC通过 Runtime 添加了一个RACDelegateProxy
属性成员变量,关于这个RAC类,笔者这里简单做下说明:RACDelegateProxy是RAC框架中对传统Delegate进行封装的一个类,它通过Runtime对Delegate进行拦截,获取到注册方法中的委托回调,同时把这个拦截到的委托回调以信号的形式(Signal)的形式逐级传递。
RAC中通过注册了UIAlertViewDelegate 对 willDismissWithButtonIndex和 clickedButtonAtIndex 的委托进行了拦截,也就是说,传统使用UIAlertView过程中,我们获取到UIAlertView对象的点击事件是由UIAlertViewDelegate进行委托回调的,而RAC中,我们则可以这么做:
UIAlertView *alertView = [[UIAlertViewalloc]
initWithTitle:@"title"message:@"message"delegate:selfcancelButtonTitle:@"cancelButtonTitle"otherButtonTitles:@"otherButtonTitles",nil];
[alertView show];
[[[alertView rac_buttonClickedSignal]deliverOn:[RACSchedulermainThreadScheduler]]
subscribeNext:^(id x) {
//UIAlertViewDelegate的委托
}];
是的,RAC中,我们可以通过-rac_buttonClickedSignal这个方法获取到委托回调的事件了,是不是方便许多了?并且在代码量上也减少了许多,这样的好处是可以防止过多的委托方法堆积在一个类中,在代码的规范、维护和更新中会起到很好的作用。
好了,现在我们进一步对RAC的UIKit进行封装,从上面的内容中,我们大体的知道了RAC关于UIAlertView做的事情,那么接下去,我们就要对UIAlertView关于RAC进行第二次的封装,以下为笔者对UIAlertView和RAC下的一个进一步的封装:
我们创建一个UIAlertView的Category拓展,拓展中,我们需要实现以下代码:
+ (RACSignal *)rac_showAlertViewWithTitle:(NSString *)title message:(NSString *)messagedelegate:(id)delegate
cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... {
va_list args;
va_start(args, otherButtonTitles);
UIAlertView *alertView = [[UIAlertViewalloc]
initWithTitle:titlemessage:message
delegate:delegatecancelButtonTitle:cancelButtonTitle
otherButtonTitles:otherButtonTitles,nil];
NSString *others =
va_arg(args,NSString *);
while (others) {
[alertView addButtonWithTitle:others];
others = va_arg(args, NSString *);
}
va_end(args);
[alertView show];
return [RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber>
subscriber) {
[[[alertView rac_buttonClickedSignal]deliverOn:[RACSchedulermainThreadScheduler]]
subscribeNext:^(id x) {
[subscriber sendNext:x];
[subscriber sendCompleted];
}];
return
nil;
}];
}
关于这个二次方法的封装,思路是这样的:关于UIAlertView的常规使用和关于RAC对UIAlertViewDelegate的Signal思想进行结合,也就是以上代码中的内容,改方法中模范UIAlertView方法的传值方式,使用va_list进行字符串的遍历,通过- addButtonWithTitle方法对遍历到的字符串进行动态添加,也就是动态添加UIAlertView的button数目,关于va_list,不清楚的童鞋可以自行百度下,或者使用过FMDB这个库的童鞋可以去FMDB的源码中看看,FMDB的作者在SQL语句的拆分上就是使用了它对方法中的字符串进行处理的,笔者这里就不多做介绍了。然后使用RACSignal的-
createSignal方法创建一个管道,把UIAlertView关于RAC的Delegate中截取的委托信号传递到最外层的出口处,这样我们就能在外层去获取到信号了。关于RACSignal,这个是RAC中对Signal这个概念的一个总结,涉及到热信号和冷信号的概念,笔者在以后的博客中会陆续的介绍它。回到我们原来的话题,那么如何使用它呢?如下:
[[UIAlertView
rac_showAlertViewWithTitle:@"test"
message:@"这是一个测试的"delegate:selfcancelButtonTitle:@"0"otherButtonTitles:@"1",@"2",@"3",@"4"]subscribeNext:^(NSNumber
*x) {
NSLog(@"x: %@", x);
}];
是不是觉得代码量大大的减少了呢?并且使用的时候感觉更加方法了?没错,这就是RAC要实现的效果,不禁是要减少代码量,防止大量代码在同一个类中堆积,同时也要让我们的代码更加整洁,维护更加简便,最重要的是要降低代码的耦合性,关于耦合性,笔者在自己的项目或者框架中是使用的MVVM模式的,关于这个,今后的博客中将陆续介绍。
总结:
关于RAC,笔者有太多话想说,在今后的博客中也将陆续为大家提供一些干货和个人使用以及学习RAC过程中的一些小心得和体会,同时也欢迎各个RAC大神和童鞋们一起学习,如果有问题也请大家不吝赐教~!
关于RAC,比如唐巧等知名的技术博主都有相应的博客,小伙伴们如果有兴趣可以自行去看看哦!笔者这里给大家提供点大神们关于RAC的博客的传送门:
http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1
http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2
http://www.raywenderlich.com/74106/mvvm-tutorial-with-reactivecocoa-part-1
http://www.raywenderlich.com/74131/mvvm-tutorial-with-reactivecocoa-part-2
http://tech.meituan.com/tag/ReactiveCocoa
http://limboy.me/
http://blog.leichunfeng.com/blog/2015/12/25/reactivecocoa-v2-dot-5-yuan-ma-jie-xi-zhi-jia-gou-zong-lan/