目录
前言
RAC(ReactiveCocoa)
函数式编程、响应式编程
Github上的开源框架
响应式编程,是一种通用的编程范式,提高了开发效率。
我的理解就是:监听事件,然后在事件发生后做相应回调处理。
/**
delegate、通知、block
KVO
*/
在命令式编程环境中,a=b+c表示将表达式的结果赋给a,而之后改变b或c的值不会影响a。
但在响应式编程中,可以做到a的值随b或c的更新而更新。
RAC框架图
1. 使用
引入(集成ReactiveCocoa框架)
pod 'ReactiveObjC'
#import <ReactiveObjC/ReactiveObjC.h>
- 常用
添加监听事件
// 给UITextFiled添加值改变事件
[[[UITextField new]rac_signalForControlEvents:UIControlEventEditingChanged]subscribeNext:^(id x) {
//
}];
等同于(简化版)
[[[UITextField new]rac_textSignal]subscribeNext:^(NSString * _Nullable x) {
// UIControlEventEditingChanged
}];
// 给按钮添加点击事件
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
添加手势
[[UIButton new]addGestureRecognizer:({
UITapGestureRecognizer *tapG=[UITapGestureRecognizer new];
[[tapG rac_gestureSignal]subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
}];
tapG;
})];
调用方法
@weakify(self)
[[self rac_signalForSelector:@selector(viewDidLoad)]subscribeNext:^(id x) {
@strongify(self)
//
} error:^(NSError * _Nullable error) {
} completed:^{
}];
dele代理
UIAlertView *alertV=[[UIAlertView alloc]initWithTitle:@"alert" message:@"content" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"sure", nil];
// 实现代理方法clickedButtonAtIndex 来自协议UIAlertViewDelegate
[[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)]subscribeNext:^(id _Nullable x) {
}];
// 等同于(简化版)
[[alertV rac_buttonClickedSignal]subscribeNext:^(NSNumber * _Nullable x) {
}];
[alertV show];
NSNotificationCenter通知
添加观察者
[[[NSNotificationCenter defaultCenter]rac_addObserverForName:@"notiName" object:nil]subscribeNext:^(NSNotification * _Nullable noti) {
// noti.name noti.object noti.object
}];
发送通知
[[NSNotificationCenter defaultCenter]postNotificationName:@"notiName" object:@[@"1",@"2"]];
KVO键值观察
// 方式一
[RACObserve([UIScrollView new], contentOffset) subscribeNext:^(id _Nullable x) {
}];
// 方式二
[[self rac_valueForKeyPath:@"age" observer:nil] subscribeNext:^(id x ){
NSLog(@"%@",x);
}];
RACSubject
// 用于view中不同button的点击事件
RACSubject *subj=[RACSubject subject];
[subj sendNext:@(100)];
// 外部调用
[subj subscribeNext:^(NSNumber *tag) {
}];
cell.clickSubject
@weakify(self);
[[cell.clickSubject takeUntil:cell.rac_prepareForReuseSignal] subscribeNext:^(NSNumber *x) {
@strongify(self);
[self clickCellBtn:[x intValue] atIndex:indexPath];
}];
值绑定
RAC(self.label.text) = _textField.rac_textSignal;
RAC(_personVM,mobile)=_phoneTF.rac_textSignal;
- 接口命令(用于MVVM)
+(RACSignal *)PostWithURL:(NSString *)urlStr parameters:(NSDictionary *)paramDic animated:(BOOL)animated{
return [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[YTNetworkTool POST:urlStr parameters:paramDic SuccessBlock:^(BOOL isOK, NSDictionary *dic) {
// 回调
[subscriber sendNext:dic];
[subscriber sendCompleted];
} failBlock:^(NSError *error) {
[subscriber sendError:error];
} animated:animated];
return [RACDisposable disposableWithBlock:^{
NSLog(@"信号发送完成或发送错误后调用,会自动执行这个block,并取消订阅信号");
}];
}]replayLazily];
}
_addRouteCommand=[[RACCommand alloc]initWithSignalBlock:^RACSignal * _Nonnull(id _Nullable input) {
//
NSDictionary *paramDic=@{@"isOpen":self.isOpen};
return [YTServiceObject PostWithURL:API_AddRouteT parameters:paramDic animated:true];
}];
执行命令(调用接口)
vm.isOpen=@(true);
[[vm.addRouteCommand execute:nil] subscribeNext:^(NSDictionary *dic) {
// dic
}];
[[[vm.addRouteCommand execute:nil]deliverOnMainThread] subscribeNext:^(NSDictionary *dic) {
// dic
}];
// 命令在执行中(用于加载 加载中...)
[[vm.addRouteCommand executing]subscribeNext:^(NSNumber * _Nullable x) {
if(x.boolValue){
// 加载 加载中...
}else{
// 隐藏 加载中...
}
}];
两个信号都收到时回调
RACSignal*signalA = [RACSignal createSignal:^RACDisposable *(id <RACSubscribe>subscriber){
NSLog(@"数据请求1");
[subscriber sendNext:@"数据请求1请求下来的数据"];
return nil;
}];
RACSignal*signalB = [RACSignal createSignal:^RACDisposable *(id <RACSubscribe>subscriber){
NSLog(@"数据请求2");
[subscriber sendNext:@"数据请求2请求下来的数据"];
return nil;
}];
[self rac_liftSelector:@Selector(updateUI) withSignalFromArray:@[signalA,signalB]];
//将 textfield 输入信号的 返回值进行修改 得到新的信号!
RACSignal *firstSignal = [self.firstTextfield.rac_textSignal map:^id(NSString *firstString) {
if (firstString.length >= 5 && firstString.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);
}];
// 绑定用户名、密码判断结果的2个信号量,如果都为真,则按钮可用
RAC(self.loginButton,enabled) = [RACSignal combineLatest:@[firstSignal,secondSignal] reduce:^(NSNumber *firstRes,NSNumber *secondRes){
return @(firstRes.boolValue && secondRes.boolValue);
}];
- 信号加条件
map 映射(拦截做额外处理)
[[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside]map:^id(id value) {
NSLog(@"value: %@",value);
return @"hello";
}]subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
filter 过滤(拦截不符合情况)
// 监听文本框的输入,而且只有大于3个长度的时候才会打印
[[self.textField.rac_textSignal filter:^BOOL(id value) {
return [value length] > 3;
}]subscribeNext:^(id x) {
NSLog(@"x:%@",x);
}];
延时
[[RACScheduler mainThreadScheduler]afterDelay:2 schedule:^{
}];
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * date) {
}];
take:2 选取前两个信号
skip:2 跳过前两个信号
repeat 无限重复执行
delay:3 延迟3s发送信号
throttle:0.5 0.5s内信号不发生变化则触发
distinctUntilChanged 不会连续发送两次相同的信号
timeout:2 超时2s后触发error
ignore:@"1" 忽略信号1
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
[self.textField endEditing:YES];
// [alertView show];
// 创建信号
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendNext:@"3"];
[subscriber sendNext:@"4"];
[subscriber sendCompleted];
return nil;
}] take:2]; // skip:2、repeat
// 发送信号
[signal subscribeNext:^(id x) {
NSLog(@"x : %@",x);
} completed:^{
NSLog(@"completed");
}];
}];
将多个不同类型的数据组合成一个元组
// 把参数中的数据包装成元组
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);
遍历数组、字典
NSArray *contentArr=@[@"hello",@"world",@"!"];
[contentArr.rac_sequence.signal subscribeNext:^(id _Nullable x) {
}];
NSDictionary *paramDic=@{@"hello":@"world"};
[paramDic.rac_sequence.signal subscribeNext:^(id _Nullable x) {
RACTwoTuple *tuple=(RACTwoTuple *)x;
// key: tuple[0]
// value: tuple[1]
}];
- 基础
信号RACSignal
// 1. 创建信号
RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3. 发送信号
[subscriber sendNext:@"唱歌"];
// 调用该方法销毁信号,否则该信号一直占用着内存
[subscriber sendCompleted];
// 3.1 发送error信号
[subscriber sendError:[NSError errorWithDomain:NSURLErrorDomain code:1001 userInfo:@{@"error":@"error message"}]];
// 4. 销毁信号完毕后回调
return [RACDisposable disposableWithBlock:^{
NSLog(@"singal已销毁");
}];
}];
// 2. 订阅信号
RACDisposable *disposable = [signalA subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
// 取消订阅
// [disposable dispose];
// 2.1 订阅error信号
[signalA subscribeError:^(NSError * _Nullable error) {
NSLog(@"%@",error);
}];
信号动作:
信号映射:map、flattenMap
信号过滤:filter、ignore、distinctUntilChanged
信号合并:combineLatest、reduce、merge、zipWith
信号连接:concat、then
信号操作时间:timeout、interval、dely
信号跳过:skip
信号取值:take、takeLast、takeUntil
信号发送顺序:donext、cocompleted
获取信号中的信号:switchToLatest
信号错误重试:retry
避免循环引用
@weakify(self)
@strongify(self)