ReactiveCocoa与函数响应式编程
在Cocoa框架下的的函数响应式编程框架。Github mac客户端产物,富含cocoa框架多种组件,提供基于时间变化的数据流的组合和变换。
命令式编程vs函数式响应式编程
命令式编程用语言提供的操作命令编写操作序列给电脑执行。
函数式编程:将电脑的运算视为函数的运算,包含如下特性:
闭包和高阶函数:函数可以作为对象,作为参数输入和作为结果返回
惰性计算:表达式值不需要绑定的时候计算,在求值程序需要产生表达式的值时进行计算。
递归:通过递归,避免定义状态变量。
不修改状态:不改变系统的外部状态,内部不定义状态变量
没有副作用:副作用指修改系统的状态,影响其它模块,函数式编程都是表达式语句,不包含任何赋值语句,变量值一 旦被指派,就永远不会改变
和命令式编程相比,函数式编程由表达式组成,内部不含状态变化,内部不含有变量,通过递归解决问题。
函数式编程好处:
1. 代码简洁,开发快速
2. 接近自然语言,易于理解
3. 代码方便管理
4. 易于”并发编程”
5. 代码的热升级
响应式编程:一种面向数据流和变化传播的编程范式,对象和对象之间的相互影响,一个对象变化关联的对象会跟着变化。一个变化抽象为一种信号,产生数据流变化。
1.对信号产生的数据流值的操作
2.信号产生的数据流的数量操作
3.信号产生的数据流的纬度操作
4.信号产生的数据流的合并处理
ReactiveCocoa使用
//target--action
[[textField rac_textSignal] subscribeNext:^(UITextField *textField) {
NSLog(@"%@",textField);
}];
[[textField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(UITextField *field) {
NSLog(@"%@",field.text);
}];
//notification
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"111" object:nil] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[[NSNotificationCenter defaultCenter] postNotificationName:@"111" object:@"111"];
//kvo
[RACObserve(self,title) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
self.title = @"xxxx";
//delegate
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"gogogo" message:@"gogogo" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:nil];
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple *tuple) {
NSLog(@"%@",tuple.first);
NSLog(@"%@",tuple.second);
NSLog(@"%@",tuple.third);
}];
[alertView show];
//冷信号 创建信号 发送信号 订阅信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"发送值"];
return [RACScopedDisposable disposableWithBlock:^{
NSLog(@"被销毁");
}];
}];
[signal subscribeNext:^(id x) {
NSLog(@"接受到信号的值");
}];
//热信号 创建 订阅 发送信号
RACSubject *subject = [RACSubject subject];
[subject subscribeNext:^(id x) {
NSLog(@"收到发送的信号");
}];
[subject subscribeNext:^(id x) {
NSLog(@"收到发送的信号");
}];
[subject sendNext:@"11111"];
//元组 遍历 效率太差 慎用
NSArray *array = @[@"123",@"1234",@"2356"];
[array.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//RACCOMMAND
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"hhahahahah"];
return [RACDisposable disposableWithBlock:^{
}];
}];
return signal;
}];
[command execute:@"111"];
//Racmuticastconnection
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"hhahahahah"];
return [RACDisposable disposableWithBlock:^{
NSLog(@"-----");
}];
}];
RACMulticastConnection *connection = [signalA publish];
[connection.signal subscribeNext:^(id x) {
NSLog(@"----------------x");
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"-----------------x");
}];
[connection connect];
//常用的宏定义
[[RACObserve(self,title) replayLazily] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//绑定
RAC(self,title) = [textField.rac_textSignal replayLazily];
[textField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[[[textField rac_textSignal] bind:^RACStreamBindBlock{
return ^RACStream *(id value,BOOL *stop) {
return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]];
};
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//MAP
[[textField.rac_textSignal map:^id(id value) {
return [NSString stringWithFormat:@"11111%@",value];
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
/// faltenmap
[[textField.rac_textSignal flattenMap:^RACStream *(id value) {
return [RACReturnSignal return:value];
}] subscribeNext:^(id x) {
NSLog(@"dsdddd%@",x);
}];
//contact 先发送信号a ,型号a发送完成 才发送 signal
RACSignal *signacContact = [signalA concat: signal];
[signacContact subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//merge 多个信号合并为一个信号,任何一个信号有新值都会调用
RACSignal *signacMerge = [signalA merge:signal];
[signacMerge subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//zipwith 合并两个信号,并把两个信号内容合并为元组
RACSignal *zipSignal = [signalA zipWith:signal];
[zipSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
//过滤
[textField.rac_textSignal filter:^BOOL(NSString *value) {
return value.length > 3;
}];
[textField.rac_textSignal ignore:@"3"];
[[textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) {
}];
[[textField.rac_textSignal skip:3] subscribeNext:^(id x) {
}];
[[textField.rac_textSignal takeLast:3] subscribeNext:^(id x) {
}];
[textField.rac_textSignal takeUntil:signal];
//秩序
[[textField.rac_textSignal doNext:^(id x) {
}] doCompleted:^{
}];
//多线程 racSchedule
[[RACScheduler scheduler] afterDelay:0 schedule:^(void) {
}];
[[signal deliverOn:[RACScheduler scheduler]] subscribeNext:^(id x) {
}];
[signal subscribeOn:[RACScheduler scheduler]];
//时间 timeout delay interval
[textField.rac_textSignal timeout:0.01 onScheduler:[RACScheduler currentScheduler]];
[textField.rac_textSignal delay:1.0];
[textField.rac_textSignal subscribeError:^(NSError *error) {
NSLog(@"error");
}];
[[RACSignal interval:1 onScheduler:[RACScheduler scheduler]] subscribeNext:^(id x) {
NSLog(@"FDF");
}];
//RAC之重复 replay replaylazily
RACSignal *signal_replay = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1111"];
return nil;
}] replayLazily];
[signal_replay subscribeNext:^(id x) {
NSLog(@"11111");
}];
[signal_replay subscribeNext:^(id x) {
NSLog(@"11111");
}];
//replaylast
RACSubject *subjectaaa = [RACSubject subject];
RACSignal *signalaaaaa = [subjectaaa replayLast];
[subjectaaa sendNext:@"1111"];
[subjectaaa sendNext:@"33333"];
[signalaaaaa subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subjectaaa sendNext:@"333"];
[signalaaaaa subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
@weakify(self)
[subjectaaa subscribeNext:^(id x) {
@strongify(self);
}];
踩坑总结
//1.RACObserve 带来的循环引用
[subjectaaa subscribeNext:^(id x) {
@strongify(self)
RACObserve(self,title);
}];
//RACSubject 进行了 map、filter、merge、combineLatest、flattenMap转换操作后,要发送complete ,不然无法释放
[[subjectaaa map:^id(NSNumber *value) {
return @([value integerValue] *3);
}] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
[subjectaaa sendNext:@(123)];
[subjectaaa sendCompleted];
//signal造成的多次订阅问题,使用reply,replayLazily让subscriber里面的代码只被执行一次
小结
RACCommand作用
重复执行一个过程
开关控制
防止重入
结果统一处理
属性
executionSignals 所有的执行信号,使用switchToLatest(获取信号中的信号)降阶
excuting 是否正在执行command
enabled 开关信号
errors 所有的错误信号,对错误进行统一处理
allowsConcurrentExecution 是否支持并发执行
Racchannel 本质是一个signal
双向通话
处理回声问题
RAC宏 : 将信号产生的值和rac括号内的值绑定
RAC(self.outputLabel,text) = self.inputTextField.rac_textSignal;
重复绑定抛出异常
takeuntil 取消绑定
什么场景使用rac
1.异步场景 (处理成功 失败 错误 变换和组合 串联 错误传递和捕获 线程处理)
2.响应事件处理(ui时间 delegate)
3.切面处理(方法调用监听)
副作用消除
在doNext中显式声明
只有订阅才产生副作用
保证信号变换返回新的不改变旧的
frp思维
需求分析
输入 输出
数据流图
小技巧
instruments RAC instruments文件夹 添加调试规则到instruments (主要是通过对rac的理解来解决问题)
nsmutableArray 通过mutableArrayForKey来取得值 监听add remove方法
错误处理:使用raccommand统一处理 catch 转发至subject
RAC是函数响应式编程框架,不是专门为mvvm提供绑定的工具。
demo