函数响应式编程可以极大地简化项目,特别是处理嵌套回调的异步事件,复杂的列表过滤和变换,或者时间相关问题.
介绍
命令式编程:以命令为主,给机器提供一条又一条的命令序列让其原封不动的执行. 函数响应式编程(FRP):使用异步数据流进行编程.FRP的思想比较难理解,需要将以往的命令式编程思想转变为响应式编程思想.即面向数据流编程.
就像面向对象思想一样,把事物都看作Stream.变量.用户输入.属性.Cache.数据结构等等.这个开始可能会很难.
在命令式编程环境中,a:=b+c表示将表达式的结果赋给a,而之后改变b或c的值不会影响a.但在响应式编程中,a的值会随着b或c的更新而更新.
电子表格程序就是响应式编程的一个例子.单元格可以包含字面值或类似"=B1+C1"的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化.这很类似观察者模式(Gof).函数响应式编程的重点是流,Stream能接受一个,甚至多个Stream为输入.你可以merge两个Stream,也可以从一个Stream中filter出你感兴趣的Events以生成一个新的Stream,还可以把一个Stream中的Data values map到一个新的Stream中.
使用RAC
RAC就是一个第三方库,他可以大大简化你的代码过程。 官方的说,ReactiveCocoa(其简称为RAC)是由GitHub开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程和响应式编程的特性。
注意 : 使用时需要引用头文件
//基本示例
#import <ReactiveCocoa/ReactiveCocoa.h>
//监听了textFild的UIControlEventEditingChanged事件,当事件发生时实现block
[[self.textFild rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(id x){
NSLog(@"change");
}];
//文字更改监听
[[self.textFild rac_textSignal] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//手势监听
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^(id x) {
NSLog(@"tap");
}];
[self.view addGestureRecognizer:tap];
//action监听
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
NSLog(@"***** 响应RAC button的点击 *****");
}];
示例代码
一.用户登录
textfield的 内容发生改变是的响应方法
1.已通过map函数将信号转换成新的信号 2.将多个信号联合起来 3.将联合起来的信号来给按钮的属性赋值
//获取 textfield 的内容改变信号 并且订阅
/*当textfield中的内容发生改变的时候回触发subscribeNext(订阅)block*/
[self.firstTextfield.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//将 textfield 输入信号的 返回值进行修改 得到新的信号!
RACSignal *firstSignal = [self.firstTextfield.rac_textSignal map:^id(NSString *firstString) {
NSInteger length = firstString.length;
if (length >= 5 && length <= 10) {
return @(YES);
}
return @(NO);
}];
RACSignal *secondSignal = [self.secondTextfield.rac_textSignal map:^id(NSString *secondString) {
if (secondString.length >5 && secondString.length < 10) {
return @(YES);
}
return @(NO);
}];
// 绑定用户名.密码判断结果的三个信号量,如果都为真,则按钮可用
RAC(self.loginButton,enabled) = [RACSignal combineLatest:@[firstSignal,secondSignal] reduce:^(NSNumber *firstRes,NSNumber *secondRes){
return @(firstRes.boolValue && secondRes.boolValue);
}];
二. 观察者模式KVO
RAC中得KVO大部分都是宏定义,所以代码异常简洁,简单来说就是RACObserve(TARGET, KEYPATH)这种形式,TARGET是监听目标,KEYPATH是要观察的属性值,这里举一个很简单的例子,如果UIScrollView滚动则输出success。
1.KVO来监听property的变化,只要被观察属性改变,block就会被执行. 2.该property必须支持KVO 3.使用必须在注册之后
UIScrollView *scrolView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 200, 400)];
scrolView.contentSize = CGSizeMake(200, 800);
scrolView.backgroundColor = [UIColor greenColor];
[self.view addSubview:scrolView];
[RACObserve(scrolView, contentOffset) subscribeNext:^(id x) {
NSLog(@"success");
}]
三. taget - action
//button
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
btn.frame = CGRectMake(20, 400, 300, 40);
btn.backgroundColor = [UIColor orangeColor];
[self.view addSubview:btn];
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(id x) {
NSLog(@"***** 响应RAC button的点击 *****");
}];
//手势
self.view.userInteractionEnabled = YES;
UITapGestureRecognizer * tap = [[UITapGestureRecognizer alloc]init];
[[tap rac_gestureSignal] subscribeNext:^(UITapGestureRecognizer * tap) {
NSLog(@"***** 响应单击手势 *****");
}];
[self.view addGestureRecognizer:tap];
四. 延时
[[RACScheduler mainThreadScheduler]afterDelay:2 schedule:^{
NSLog(@"***** first 延时rac写法 *****");
}];
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) {
NSLog(@"***** second 延时rac写法 *****");
}];
五. 通知
特别注意:RAC中的通知不需要remove observer,因为在rac_add方法中他已经写了remove。
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"postData" object:nil] subscribeNext:^(NSNotification *notification) {
NSLog(@"%@", notification.name);
NSLog(@"%@", notification.object);
}];
六. 将多个不同类型的数据组合成一个元组
// 把参数中的数据包装成元组
RACTuple *tuple = RACTuplePack(@"xmg",@20,@"m",@(999),@[@"a"],@{@"key":@"value"});
RACTupleUnpack(NSString *name,NSNumber *age,NSString *sex,NSNumber *price,NSArray *arr,NSDictionary *dic) = tuple;
NSLog(@"name:%@ age:%@ sex:%@ price:%@ arr:%@ dic:%@",name,age,sex,price,arr,dic);
七. 使用 #define RAC(TARGET, ...)宏
//以下代码 可以将_label的text属性和后面的信号绑定信号内容发生改变的时候自动更新
RAC(_label,text) = self.textField.rac_textSignal;