[iOS] 响应式编程开发-ReactiveCocoa(一)
什么是响应式编程
响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
例如,在命令式编程环境中,a:=b+c表示将表达式的结果赋给a,而之后改变b或c的值不会影响a。但在响应式编程中,a的值会随着b或c的更新而更新。
电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
响应式编程最初是为了简化交互式用户界面的创建和实时系统动画的绘制而提出来的一种方法,但它本质上是一种通用的编程范式。
什么是ReactiveCocoa
ReactiveCocoa (RAC) 是一个Objective-C语言内实现响应式编程的框架。
RAC提供了大量的能够完成发送 value's stream 的API。
RAC通过使用信号量(RACSignal)来完成获取当前值和未来值的功能,而不像传统的程序开发一样需要声明大量的变量。
Josh Abernathy这样解释它:
- 程序接收输入产生输出。输出就是对输入做了一些事的结果。输入,转换,输出,完成。
- 输入是应用动作的全部来源。点击、键盘事件、定时器事件、GPS时间、网络请求响应都算是输入。这些事件被传递到应用中,应用将他们以某种方式混合,产生了结果:就是输出。
- 输出通常会改变应用的UI。开关状态变化、列表有了新的元素都是UI变化。也有可能让磁盘上某个文件产生变化,或者产生一个API请求,这都是应用的输出。
- 但不像传统的输入输出设计,应用的输入输出可以产生很多次。应用打开后,不只是一个简单的 输入→工作→输出 就构成了一个生命周期。应用经常有大量的输入并基于这些输入产生输出。
基本的使用方法
例如,我们目前想要实现一个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信号量具有如下功能
- 异步控制或事件驱动的数据源:Cocoa编程中大多数时候会关注用户事件或应用状态改变产生的响应。
- 链式以来操作:网络请求是最常见的依赖性样例,前一个对server的请求完成后,下一个请求才能构建。
- 并行独立动作:独立的数据集要并行处理,随后还要把他们合并成一个最终结果。这在Cocoa中很常见,特别是涉及到同步动作时。
RACSignal会触发它们的subscriber三种不同类型的事件:
- 下一个事件从stream中提供一个新值。不像Cocoa集合,它是完全可用的,甚至一个signal可以包含
nil
。 - 错误事件会在一个signal结束之前被标示出来这里有一个错误。这种事件可能包含一个
NSError
对象来标示什么发生了错误。错误必须被特殊处理——错误不会被包含在stream的值里面。 - 完成事件标示signal成功结束,不会再有新的值会被加入到stream当中。完成事件也必须被单独控制——它不会出现在stream的值里面。
一个RACSignal信号量的生命周期由很多下一个(next)
事件和一个错误(error)
或完成(completed)
事件组成(后两者不同时出现)。
总结对比
RAC 与 KVO
Key-Value Observing是Cocoa所有魔法的核心,它被广泛应用在ReactiveCocoa对于属性变化的影响动作中。然而KVO用起来即不简单也不开心:它的API有很多过度设计的参数,以及缺乏方便的block方式调用。
RAC 与 Bindings
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就是一种警示,提醒人们好的点子也可以从看似不太可能的地方传播过来,这样的新鲜思想对解决类似的问题也会有完全不同的方法呢。
下一节,我们来一起看一下如何使用RAC来完成异步的功能.
[iOS] 响应式编程开发-ReactiveCocoa(一)的更多相关文章
- [iOS] 响应式编程开发-ReactiveCocoa(二)
RAC实现图片下载功能 在实现异步RAC下载图片的过程中,需要注意以下几点: • 通过 NSURLConnection 对象的 +(RACSignal *)rac_sendAsynchronousRe ...
- iOS响应式编程:ReactiveCocoa vs RxSwift 选谁好
转载: iOS响应式编程:ReactiveCocoa vs RxSwift 选谁好 内容来自stack overflow的一个回答:ReactiveCocoa vs RxSwift – pros an ...
- IOS响应式编程框架ReactiveCocoa(RAC)使用示例
ReactiveCocoa是响应式编程(FRP)在iOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# :在网上看了几 ...
- IOS响应式编程框架ReactiveCocoa(RAC)使用示例-备
ReactiveCocoa是响应式编程(FRP)在IOS中的一个实现框架,它的开源地址为:https://github.com/ReactiveCocoa/ReactiveCocoa# :在网上看了几 ...
- ReactiveCocoa,最受欢迎的iOS函数响应式编程库(2.5版),没有之一!
简介 项目主页: ReactiveCocoa 实例下载: https://github.com/ios122/ios122 简评: 最受欢迎,最有价值的iOS响应式编程库,没有之一!iOS MVVM模 ...
- 使用ReactiveCocoa实现iOS平台响应式编程
使用ReactiveCocoa实现iOS平台响应式编程 ReactiveCocoa和响应式编程 在说ReactiveCocoa之前,先要介绍一下FRP(Functional Reactive Prog ...
- [转]使用ReactiveCocoa实现iOS平台响应式编程
原文:http://www.itiger.me/?p=38 使用ReactiveCocoa实现iOS平台响应式编程 ReactiveCocoa和响应式编程 在说ReactiveCocoa之前,先要介绍 ...
- iOS开发--Swift RAC响应式编程初探
时间不是很充足, 先少说点, RAC的好处是响应式编程, 不需要自己去设置代理委托, target, 而是主要以信息流(signal), block为主, 看到这里激动吧, 它可以帮你监听你的事件, ...
- iOS开发--Swift RAC响应式编程
时间不是很充足, 先少说点, RAC的好处是响应式编程, 不需要自己去设置代理委托, target, 而是主要以信息流(signal), block为主, 看到这里激动吧, 它可以帮你监听你的事件, ...
随机推荐
- 【java】基础中的杂乱总结(一)
1 构造代码块 作用:给对象进行初始化.对象一建立就运行,并且优先于构造函数执行 构造函数是给所有对象进行统一初始化,构造函数是给对应的对象初始化 package package1; class Pe ...
- OpenGL编程指南第版本学习笔记 --- OpenGL程序实现过程(win32 + OpenGL)
1. 先上代码 头文件glCommon.h #include <GL/glew.h> #include <GL/GL.h> #include <GL/GLU.h> ...
- 文本格式ANSI,Unicode等有什么区别
首先DBCS是亚洲的字符集,包含了ANSI,ANSI也就是ASCII值为0-255之间的字符,当字符为ANSI时,存放于文件中占用的是一个字节.如果是非ANSI的呢,则占用两字节.用VB的ASC函数可 ...
- HDU2199,HDU2899,HDU1969,HDU2141--(简单二分)
二分是一种很有效的减少时间开销的策略, 我觉得单列出二分专题有些不太合理, 二分应该作为一中优化方法来考虑 这几道题都是简单的使用了二分方法优化, 二分虽然看似很简单, 但一不注意就会犯错. 在写二分 ...
- linux和windows之间上传 下载文件 非ftp方式
用 命令 rz 上传 sz 下载 文件夹加上 -r rz上传替换时用 -y 谁用谁知道 两台linux传 : scp -r 文件夹 username@ip:路径 (如果传输文件就 ...
- ubuntu上的mysql数据库双机备份设置
配置环境: myslq 5.5.3 + ubuntu server 12.04 一.配置MySQL主服务器(192.168.0.1) 1.增加一个账号专门用于同步 1 mysql>grant r ...
- 报错:“不是有效的Win32应用程序”的解决办法
Win7.Win8下用VS2013编译完的程序,拿到32位WindowsXP虚拟机下运行有时候会报错:
- Photoshop学习之路
网易云课堂中有的提升课程:http://study.163.com/course/courseMain.htm?courseId=765016&6925ecaa9614a750=Individ ...
- abstract class 与interface
一.抽象类(absteact class) 特点: 1.抽象方法只作说明,而不包含实现,可以看成是没有实现体的虚方法 2.抽象类不能被实例化.除此之外,具有类的其他特性 3.抽象类可以但不是必须有抽象 ...
- Android编程之SparseArray<E>详解
最近编程时,发现一个针对HashMap<Integer, E>的一个提示: 翻译过来就是:用SparseArray<E>来代替会有更好性能.那我们就来看看源码中SparseAr ...