响应式编程是一种面向数据流和变化传播的编程范式。这意味着能够在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值经过数据流进行传播。编程
例如,在命令式编程环境中,a:=b+c表示将表达式的结果赋给a,而以后改变b或c的值不会影响a。但在响应式编程中,a的值会随着b或c的更新而更新。
电子表格程序就是响应式编程的一个例子。单元格能够包含字面值或相似"=B1+C1"的公式,而包含公式的单元格的值会依据其余单元格的值的变化而变化。网络
响应式编程最初是为了简化交互式用户界面的建立和实时系统动画的绘制而提出来的一种方法,但它本质上是一种通用的编程范式。并发
ReactiveCocoa (RAC) 是一个Objective-C语言内实现响应式编程的框架。框架
RAC提供了大量的可以完成发送 value's stream 的API。异步
RAC经过使用信号量(RACSignal)来完成获取当前值和将来值的功能,而不像传统的程序开发同样须要声明大量的变量。编程语言
Josh Abernathy这样解释它:ide
例如,咱们目前想要实现一个NSString对象能够一直绑定到最新的时间,即便字符串发生了变化,也不该该是再去使用时间去从新赋值了。函数式编程
听起来特别像Objective-C语言内的KVO特性,可是这并非具备压倒性优点的那个方法:函数
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context;
那么咱们如今使用RAC框架来实现这个功能动画
***.h
//用来标识时间变量 @property (nonatomic ,strong) NSDate *time; //用来标识文字显示区域 @property (nonatomic ,strong) IBOutlet UILabel *label;
***.m
//申请注册一个每一个1秒将会在主线程执行一次的信号量 RACSignal *repeatSignal = [[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]] repeat]; //为信号量添加执行代码端 [repeatSignal subscribeNext: ^(NSDate* time){ self.time = time; }]; //申请注册一个时间属性的信号量 RACSignal *timeSignal = [self rac_valuesForKeyPath:@"time" observer:self]; //为信号量添加执行代码端 [timeSignal subscribeNext:^(NSDate* time) { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"HH:mm:ss"]; self.label.text = [formatter stringFromDate:time]; RELEASESAFELY(formatter); }];
一样和Objective-C语言内的KVO特性不一样的是,RACSignal信号量能够进行过滤设置。
以上边的例子的话,咱们加一个功能。
•获取偶数秒的时间
那么信号量部分的代码能够写为
//申请注册一个时间属性的信号量 RACSignal *timeSignal = [self rac_valuesForKeyPath:@"time" observer:self]; //为信号量添加过滤block [[timeSignal filter:^BOOL(NSDate* time) { //获取描述的时间 NSDateComponents *com = [[NSCalendar currentCalendar] components:NSCalendarUnitSecond fromDate:time]; return com.second % 2 == 0; }] subscribeNext:^(NSDate* time) { NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; [formatter setDateFormat:@"HH:mm:ss"]; self.label.text = [formatter stringFromDate:time]; RELEASESAFELY(formatter); }];
信号量还能够用来导出对应的状态。与Objective-C语言KVO特性不一样的是,RAC可以为新的值设置其余的属性。
那么咱们仍是举个功能例子
•在注册用户时,当用户密码与确认密码相同时,在Label中显示"1",不相同时,显示"0";
•如图所示
传统方式代码
- (BOOL)isValid { return [self.password.text length] > 0 && [self.confirm.text length] > 0 && [self.password.text isEqual:self.confirm.text]; } #pragma mark - UITextFieldDelegate - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string { self.label.text = @(self.isValid).description; return YES; }
咱们发现逻辑被放在了不少方法里,零碎地摆放在view controller里,经过处处散布到delegate里的self.label.text = @(self.isValid).description;
方法在页面的生命周期中被调用。
那么RAC实现方式的代码
RACSignal *passworkSignal = self.password.rac_textSignal; RACSignal *confirmSignal = self.confirm.rac_textSignal; RACSignal *combineSignal = [RACSignal combineLatest:@[passworkSignal,confirmSignal] reduce:^(NSString *password, NSString *confirm){ ; return @([password isEqualToString:confirm]).description; }]; RAC(self,label.text) = combineSignal;
全部对于的输入都整合在了一块儿。每次不论哪一个输入框被修改了,用户的输入都会被reduce成一个字符串的值,而后就能够自动来控制注册按钮的可用状态了。
RAC除了可以完成KVO的功能以外,一样能够完成按钮等用户响应的交互功能
•完成一个点击按钮弹出Alert的功能
•如图所示
传统方式实现的代码
- (void)viewDidLoad { [super viewDidLoad]; //添加触发事件 [self.btn addTarget:self action:@selector(didClick) forControlEvents:UIControlEventTouchUpInside]; } //点击按钮触发的回调方法 - (void)didClick { //建立弹出窗口 UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"蓝鸥" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"肯定", nil]; [alertView show]; RELEASESAFELY(alertView); }
RAC方式实现的代码以下
//添加触发信号量 self.btn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { //建立弹出窗口 UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"蓝鸥" message:nil delegate:nil cancelButtonTitle:nil otherButtonTitles:@"肯定", nil]; [alertView show]; RELEASESAFELY(alertView); return [RACSignal empty]; }];
经过以上的代码,RACSignal信号量具备以下功能
RACSignal会触发它们的subscriber三种不一样类型的事件:
nil
。NSError
对象来标示什么发生了错误。错误必须被特殊处理——错误不会被包含在stream的值里面。 一个RACSignal信号量的生命周期由不少下一个(next)
事件和一个错误(error)
或完成(completed)
事件组成(后二者不一样时出现)。
Key-Value Observing是Cocoa全部魔法的核心,它被普遍应用在ReactiveCocoa对于属性变化的影响动做中。然而KVO用起来即不简单也不开心:它的API有不少过分设计的参数,以及缺少方便的block方式调用。
Bindings也是黑魔法。
虽然对OS X控制的要点就是Bindings,可是它的意义在近年来愈来愈没那么重要了,由于焦点已经移动到了iOS和UIKit这些Bindings不支持的东西身上。Bindings替代了大量的模版胶水代码,容许在Interface Builder中完成编码,但严格上说仍是比较有局限性的,而且_没法_debug。RAC提供了一种简洁易懂、扩展性强的以代码为基础的API来运行在iOS上,目标就是取代全部在OS X能用Bindings实现的神奇功能。
Objective-C在C的核心上吸取了Smalltalk的思想创建而成,但哲学理念上已经超越了它本来来源的血统。
@protocol
是对C++多重继承的拒绝,顺应抽象数据的类型范式是对Java Interface
的吸取。Objective-C 2.0引入了@property / @synthesize
则灵感来自C#的 get; set;
方法对getter和setter的速记(就语法上来讲,这也是NeXTSETP强硬路线坚持者常常辩论的一点)。Block给这门语言带来了函数式编程的好处,可使用Grand Central Dispatch——来自Fortran / C / C++ standard OpenMP思想而成的基于队列的并发API。下标和对象字面量都是像Ruby、Javascript这样的脚本语言的标准特性,现在也由一个Clang插件被带入了Objective-C的世界里。
ReactiveCocoa则给Objective-C带来了函数响应式编程的健康药剂。它自己也是受C#的Rx library、Clojure和Elm的影响发展而成。
好的点子会传染。ReactiveCocoa就是一种警示,提醒人们好的点子也能够从看似不太可能的地方传播过来,这样的新鲜思想对解决相似的问题也会有彻底不一样的方法呢。