ReactiveCocoa与函数响应式编程
在Cocoa框架下的的函数响应式编程框架。Github mac客户端产物,富含cocoa框架多种组件,提供基于时间变化的数据流的组合和变换。@H_502_4@
命令式编程vs函数式响应式编程
命令式编程用语言提供的操作命令编写操作序列给电脑执行。
函数式编程:将电脑的运算视为函数的运算,包含如下特性:
闭包和高阶函数:函数可以作为对象,作为参数输入和作为结果返回
惰性计算:表达式值不需要绑定的时候计算,在求值程序需要产生表达式的值时进行计算。
递归:通过递归,避免定义状态变量。
不修改状态:不改变系统的外部状态,内部不定义状态变量
没有副作用:副作用指修改系统的状态,影响其它模块,函数式编程都是表达式语句,不包含任何赋值语句,变量值一 旦被指派,就永远不会改变
和命令式编程相比,函数式编程由表达式组成,内部不含状态变化,内部不含有变量,通过递归解决问题。
函数式编程好处:
1. 代码简洁,开发快速
2. 接近自然语言,易于理解
3. 代码方便管理
4. 易于”并发编程”
5. 代码的热升级
响应式编程:一种面向数据流和变化传播的编程范式,对象和对象之间的相互影响,一个对象变化关联的对象会跟着变化。一个变化抽象为一种信号,产生数据流变化。
1.对信号产生的数据流值的操作
2.信号产生的数据流的数量操作
3.信号产生的数据流的纬度操作
4.信号产生的数据流的合并处理@H_502_4@
ReactiveCocoa使用@H_502_4@
//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);
}];
踩坑总结@H_502_4@
//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作用
重复执行一个过程
开关控制
防止重入
结果统一处理@H_502_4@
属性
executionSignals 所有的执行信号,使用switchToLatest(获取信号中的信号)降阶
excuting 是否正在执行command
enabled 开关信号
errors 所有的错误信号,对错误进行统一处理
allowsConcurrentExecution 是否支持并发执行@H_502_4@
Racchannel 本质是一个signal
双向通话
处理回声问题@H_502_4@
RAC宏 : 将信号产生的值和rac括号内的值绑定
RAC(self.outputLabel,text) = self.inputTextField.rac_textSignal;
重复绑定抛出异常
takeuntil 取消绑定@H_502_4@
什么场景使用rac
1.异步场景 (处理成功 失败 错误 变换和组合 串联 错误传递和捕获 线程处理)
2.响应事件处理(ui时间 delegate)
3.切面处理(方法调用监听)@H_502_4@
副作用消除
在doNext中显式声明
只有订阅才产生副作用
保证信号变换返回新的不改变旧的@H_502_4@
frp思维
需求分析
输入 输出
数据流图@H_502_4@
小技巧
instruments RAC instruments文件夹 添加调试规则到instruments (主要是通过对rac的理解来解决问题)
nsmutableArray 通过mutableArrayForKey来取得值 监听add remove方法
错误处理:使用raccommand统一处理 catch 转发至subject
RAC是函数响应式编程框架,不是专门为mvvm提供绑定的工具。
demo@H_502_4@