ReactiveCocoa学习总结
最近一直断断续续学习关于ReactiveCocoa的知识内容,对于它的一些基础内容将通过本文进行一个总结,主要是一些入门知识
一:RACSignal一些运用
@interface RACSignalTestViewController ()
@property(nonatomic,strong)RACSignal *mySignal,*secondSingl;
@end
-(RACSignal *)mySignal
{
if (!_mySignal) {
_mySignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@10];
[subscriber sendCompleted];
return nil;
}];
} return _mySignal;
} -(RACSignal *)secondSingl
{
if (!_secondSingl) {
_secondSingl=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@20];
[subscriber sendCompleted];
return nil;
}];
}
return _secondSingl;
}
先创建两个RACSignal的内容;将用于接下来的一些操作
//map运用 返回的值作为next的参数
[[self.mySignal map:^id(NSNumber *value) {
return [value integerValue]>5?@"踏浪帅":@"有点小";
}] subscribeNext:^(NSString *str) {
NSLog(@"map处理完成的值为:%@",str);
}]; //filter:过滤信号,使用它可以获取满足条件的信号.
[[self.mySignal filter:^BOOL(NSNumber *item) {
return [item integerValue]>5;
}] subscribeNext:^(NSNumber *x) {
NSLog(@"filter当前的值%ld",[x integerValue]);
}]; //ignore:忽略完某些值的信号.当答合被ignore的值时就不会执行next
[[self.mySignal ignore:@10] subscribeNext:^(id x) {
NSLog(@"ignore当前的值:%@",x);
}]; //distinctUntilChanged:当上一次的值和当前的值有明显的变化就会发出信号,否则会被忽略掉。
[[self.mySignal distinctUntilChanged] subscribeNext:^(id x) {
NSLog(@"distinctUntilChanged当前的值:%@",x);
}]; //take:从开始一共取N次的信号
//takeLast:取最后N次的信号,前提条件,订阅者必须调用完成,因为只有完成,就知道总共有多少信号.
//takeUntil:(RACSignal *):获取信号直到某个信号执行完成
//skip:(NSUInteger):跳过几个信号,不接受。
运行结果:
MobileProject[885:18784] map处理完成的值为:踏浪帅
2016-02-02 16:03:07.643 MobileProject[885:18784] filter当前的值10
2016-02-02 16:03:07.644 MobileProject[885:18784] distinctUntilChanged当前的值:10
1.2:一些组合的操作
//concat:按一定顺序拼接信号,当多个信号发出的时候,有顺序的接收信号
RACSignal *concatSignal=[self.mySignal concat:self.secondSingl];
[concatSignal subscribeNext:^(id x) {
NSLog(@"concat拼接的值:%@",x);
}]; //then:用于连接两个信号,当第一个信号完成,才会连接then返回的信号
[[self.mySignal then:^RACSignal *{
@strongify(self);
return self.secondSingl;
}] subscribeNext:^(id x) {
//// 只能接收到第二个信号的值,也就是then返回信号的值
NSLog(@"then当前的值为:%@",x);
}]; //merge:把多个信号合并为一个信号,任何一个信号有新值的时候就会调用
RACSignal *mergeSignal=[self.mySignal merge:self.secondSingl];
[mergeSignal subscribeNext:^(id x) {
NSLog(@"merge当前的值为:%@",x);
}]; //combineLatest:将多个信号合并起来,并且拿到各个信号的最新的值,必须每个合并的signal至少都有过一次sendNext,两个信号的内容合并成一个元组RACTuple,才会触发合并的信号。
RACSignal *combineLatestSignal=[self.mySignal combineLatestWith:self.secondSingl];
[combineLatestSignal subscribeNext:^(id x) {
NSLog(@"combineLastest当前的值为:%@",x);
}]; //zipWith:把两个信号压缩成一个信号,只有当两个信号同时发出信号内容时,并且把两个信号的内容合并成一个元组RACTuple,才会触发压缩流的next事件。
RACSignal *zipWithSignal=[self.mySignal zipWith:self.secondSingl];
[zipWithSignal subscribeNext:^(RACTuple *tuple) {
NSLog(@"zipWith当前的值为:%@",tuple); NSLog(@"zipWith中的RACTuple共有几个值:%ld",tuple.count); NSLog(@"zipWith中的RACTuple第一个值为:%@",tuple.first); NSLog(@"zipWith中的RACTuple最后一个值为:%@",tuple.last);
}]; //reduce聚合:用于信号发出的内容是元组,把信号发出元组的值聚合成一个值 特别是combineLatestWith,zipWith这种返回元组的RACTuple,reduceblcok中的参数,有多少信号组合,reduceblcok就有多少参数,每个参数就是之前信号发出的内容
RACSignal *reduceSignal=[RACSignal combineLatest:@[self.mySignal,self.secondSingl] reduce:^id(NSNumber *num1 ,NSNumber *num2){
NSLog(@"combineLastest结合reduct的第一个值为:%ld 第二个值为:%ld",[num1 integerValue],[num2 integerValue]);
return ([num1 integerValue]>10&&[num2 integerValue]>15)?@"两个都符合要求":@"都没有答合要求";
}];
[reduceSignal subscribeNext:^(id x) {
NSLog(@"reduce当前的值为:%@",x);
}];
运行结果:
MobileProject[885:18784] concat拼接的值:10
2016-02-02 16:03:07.645 MobileProject[885:18784] concat拼接的值:20
2016-02-02 16:03:07.646 MobileProject[885:18784] then当前的值为:20
2016-02-02 16:03:07.646 MobileProject[885:18784] merge当前的值为:10
2016-02-02 16:03:07.646 MobileProject[885:18784] merge当前的值为:20
2016-02-02 16:03:07.647 MobileProject[885:18784] combineLastest当前的值为:<RACTuple: 0x7ff5ca606860> (
10,
20
)
2016-02-02 16:03:07.648 MobileProject[885:18784] zipWith当前的值为:<RACTuple: 0x7ff5ca60ab80> (
10,
20
)
2016-02-02 16:03:07.648 MobileProject[885:18784] zipWith中的RACTuple共有几个值:2
2016-02-02 16:03:07.648 MobileProject[885:18784] zipWith中的RACTuple第一个值为:10
2016-02-02 16:03:07.648 MobileProject[885:18784] zipWith中的RACTuple最后一个值为:20
2016-02-02 16:03:07.649 MobileProject[885:18784] combineLastest结合reduct的第一个值为:10 第二个值为:20
2016-02-02 16:03:07.720 MobileProject[885:18784] reduce当前的值为:都没有答合要求
1.3:其它一些操作:
//doNext: 执行Next之前,会先执行这个Block,可以做一些初使化的功能,doCompleted: 执行sendCompleted之前,会先执行这个Block
[[[self.mySignal doNext:^(id x) {
NSLog(@"doNext当前的值:%@",x);
}] doCompleted:^{
NSLog(@"doComplete 执行到了");
}] subscribeNext:^(id x) {
NSLog(@"测试doNext,doComplete的值:%@",x);
}]; //switchToLatest:用于signalOfSignals(信号的信号),有时候信号也会发出信号,会在signalOfSignals中,获取signalOfSignals发送的最新信号。
RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject]; // 获取信号中信号最近发出信号,订阅最近发出的信号。
// 注意switchToLatest:只能用于信号中的信号
[signalOfSignals.switchToLatest subscribeNext:^(id x) { NSLog(@"switchToLatest%@",x);
}];
[signalOfSignals sendNext:signal];
[signal sendNext:@1]; //deliverOn: 内容传递切换到制定线程中,副作用在原来线程中,把在创建信号时block中的代码称之为副作用。
//subscribeOn: 内容传递和副作用都会切换到制定线程中。' //timeout:超时,可以让一个信号在一定的时间后,自动报错。
[[self.mySignal timeout:1 onScheduler:[RACScheduler currentScheduler]]
subscribeNext:^(id x) {
NSLog(@"timeout当前的值:%@",x);
} error:^(NSError *error) {
NSLog(@"timeout 超时了");
}]; //delay 延迟发送next。
[[self.mySignal delay:2] subscribeNext:^(id x) {
NSLog(@"delay执行%@",x);
}]; //retry重试 :只要失败,就会重新执行创建信号中的block,直到成功.
__block int i = 0;
[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { if (i == 10) {
[subscriber sendNext:@100];
}else{
NSLog(@"接收到错误");
[subscriber sendError:nil];
}
i++; return nil; }] retry] subscribeNext:^(id x) { NSLog(@"retry当前的值:%@",x); } error:^(NSError *error) { }]; //普通执行多次订阅
RACSignal *oneSignal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
return nil;
}] replay];
[oneSignal subscribeNext:^(id x) {
NSLog(@"第一个没有replay执行的内容:%@",x);
}];
[oneSignal subscribeNext:^(id x) {
NSLog(@"第二个没有replay执行的内容:%@",x);
}]; //replay重放:当一个信号被多次订阅,反复播放内容
RACSignal *replaySignal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
return nil;
}] replay]; [replaySignal subscribeNext:^(id x) {
NSLog(@"replay第一个订阅者%@",x);
}]; [replaySignal subscribeNext:^(id x) {
NSLog(@"replay第二个订阅者%@",x);
}]; //interval 定时:每隔一段时间发出信号
[[RACSignal interval:1 onScheduler:[RACScheduler currentScheduler]] subscribeNext:^(id x) {
NSLog(@"interval一直在执行:%@",x);
}]; //throttle节流:当某个信号发送比较频繁时,可以使用节流,在某一段时间不发送信号内容,过了一段时间获取信号的最新内容发出。
运行结果:
2016-02-02 16:03:07.720 MobileProject[885:18784] doNext当前的值:10
2016-02-02 16:03:07.720 MobileProject[885:18784] 测试doNext,doComplete的值:10
2016-02-02 16:03:07.721 MobileProject[885:18784] doComplete 执行到了
2016-02-02 16:03:07.721 MobileProject[885:18784] switchToLatest1
2016-02-02 16:03:07.722 MobileProject[885:18784] timeout当前的值:10
2016-02-02 16:03:07.722 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.723 MobileProject[885:18784] 第一个没有replay执行的内容:1
2016-02-02 16:03:07.723 MobileProject[885:18784] 第一个没有replay执行的内容:2
2016-02-02 16:03:07.723 MobileProject[885:18784] 第二个没有replay执行的内容:1
2016-02-02 16:03:07.723 MobileProject[885:18784] 第二个没有replay执行的内容:2
2016-02-02 16:03:07.724 MobileProject[885:18784] replay第一个订阅者1
2016-02-02 16:03:07.724 MobileProject[885:18784] replay第一个订阅者2
2016-02-02 16:03:07.724 MobileProject[885:18784] replay第二个订阅者1
2016-02-02 16:03:07.724 MobileProject[885:18784] replay第二个订阅者2
2016-02-02 16:03:07.838 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.838 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.838 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.838 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.839 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.865 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.865 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.865 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.866 MobileProject[885:18784] 接收到错误
2016-02-02 16:03:07.866 MobileProject[885:18784] retry当前的值:100
2016-02-02 16:03:08.726 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:08 +0000
2016-02-02 16:03:09.724 MobileProject[885:18784] delay执行10
2016-02-02 16:03:09.725 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:09 +0000
2016-02-02 16:03:10.726 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:10 +0000
2016-02-02 16:03:11.724 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:11 +0000
2016-02-02 16:03:12.727 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:12 +0000
2016-02-02 16:03:13.725 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:13 +0000
2016-02-02 16:03:14.730 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:14 +0000
2016-02-02 16:03:15.726 MobileProject[885:18784] interval一直在执行:2016-02-02 08:03:15 +0000
二:关于RACCommand的一些知识
@interface TestRacViewController ()
@property(nonatomic,strong)UITextField *userNameText;
@property(strong,nonatomic)NSString *username;
@property(nonatomic,strong)UIButton *loginButton,*racCommendButton,*errCommendButton,*mainThreadButton,*netWorkButton,*testButton; @property(nonatomic,strong)RACCommand *otherMyRaccomand,*mainThreadCommend,*netWorkCommend,*testPropertyCommend;
@end
//布局
-(void)loadPage
{
if (!self.userNameText) {
self.userNameText=[[UITextField alloc]init];
self.userNameText.backgroundColor=[UIColor whiteColor];
self.userNameText.placeholder=@"输入用户名";
[self.view addSubview:self.userNameText];
[self.userNameText mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(15);
make.top.mas_equalTo(self.view.mas_top).with.offset(70);
make.right.mas_equalTo(self.view.mas_right).with.offset(-15);
make.height.mas_equalTo(@40);
}];
} if(!self.loginButton)
{
self.loginButton=[[UIButton alloc]init];
[self.loginButton setTitle:@"响应" forState:UIControlStateNormal];
self.loginButton.backgroundColor=[UIColor blueColor];
[self.view addSubview:self.loginButton];
[self.loginButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(15);
make.top.mas_equalTo(self.userNameText.mas_bottom).with.offset(20);
make.right.mas_equalTo(self.view.mas_right).with.offset(-15);
make.height.mas_equalTo(@40);
}];
} if(!self.racCommendButton)
{
self.racCommendButton=[[UIButton alloc]init];
[self.racCommendButton setTitle:@"RacCommend测试" forState:UIControlStateNormal];
self.racCommendButton.backgroundColor=[UIColor blueColor];
[self.view addSubview:self.racCommendButton];
[self.racCommendButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(15);
make.top.mas_equalTo(self.loginButton.mas_bottom).with.offset(20);
make.right.mas_equalTo(self.view.mas_right).with.offset(-15);
make.height.mas_equalTo(@40);
}];
} if(!self.errCommendButton)
{
self.errCommendButton=[[UIButton alloc]init];
[self.errCommendButton setTitle:@"ERROR测试" forState:UIControlStateNormal];
self.errCommendButton.backgroundColor=[UIColor blueColor];
[self.view addSubview:self.errCommendButton];
[self.errCommendButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(15);
make.top.mas_equalTo(self.racCommendButton.mas_bottom).with.offset(20);
make.right.mas_equalTo(self.view.mas_right).with.offset(-15);
make.height.mas_equalTo(@40);
}];
} if(!self.mainThreadButton)
{
self.mainThreadButton=[[UIButton alloc]init];
[self.mainThreadButton setTitle:@"主线程上运行" forState:UIControlStateNormal];
self.mainThreadButton.backgroundColor=[UIColor blueColor];
[self.view addSubview:self.mainThreadButton];
[self.mainThreadButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(15);
make.top.mas_equalTo(self.errCommendButton.mas_bottom).with.offset(20);
make.right.mas_equalTo(self.view.mas_right).with.offset(-15);
make.height.mas_equalTo(@40);
}];
} if(!self.netWorkButton)
{
self.netWorkButton=[[UIButton alloc]init];
[self.netWorkButton setTitle:@"属性测试" forState:UIControlStateNormal];
self.netWorkButton.backgroundColor=[UIColor blueColor];
[self.view addSubview:self.netWorkButton];
[self.netWorkButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(15);
make.top.mas_equalTo(self.mainThreadButton.mas_bottom).with.offset(20);
make.right.mas_equalTo(self.view.mas_right).with.offset(-15);
make.height.mas_equalTo(@40);
}];
} if(!self.testButton)
{
self.testButton=[[UIButton alloc]init];
[self.testButton setTitle:@"测试特性" forState:UIControlStateNormal];
self.testButton.backgroundColor=[UIColor blueColor];
[self.view addSubview:self.testButton];
[self.testButton mas_makeConstraints:^(MASConstraintMaker *make) {
make.left.mas_equalTo(self.view.mas_left).with.offset(15);
make.top.mas_equalTo(self.netWorkButton.mas_bottom).with.offset(20);
make.right.mas_equalTo(self.view.mas_right).with.offset(-15);
make.height.mas_equalTo(@40);
}];
}
}
一些定义
//创建一个信号代码
-(RACSignal *)testSignal
{
RACSignal *signal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
NSLog(@"创建信号");
[subscriber sendNext:@"Hi 我是一个信号"];
[subscriber sendCompleted];
return nil;
}];
return signal;
} //创建一个命令响应
-(RACCommand *)testCommend
{
@weakify(self);
RACCommand *myCommend=[[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
RACSignal *authSignal=[RACSignal empty];
@strongify(self); authSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
//可以根据要求加条件进行判断是否创建信号
if ([self.username isEqualToString:@"wjy"]) {
NSLog(@"符合要求");
[subscriber sendNext:@"我完成的命令响应"];
[subscriber sendCompleted];
}
else
{
//错误
[subscriber sendError:[NSError errorWithDomain:@"报错了" code:1 userInfo:nil]];
}
return nil;
}];
//materialize是为了处理拿不到error的问题
return [authSignal materialize];
}]; return myCommend;
} //创建属性的RACCommand
-(RACCommand *)otherMyRaccomand
{
if (!_otherMyRaccomand) {
_otherMyRaccomand=[[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"执行到新的RACCommand");
RACSignal *otherSignal=[RACSignal empty];
otherSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
if ([self.username isEqualToString:@"wjy"]) {
[subscriber sendNext:@"我肯定可以运行的"];
[subscriber sendCompleted];
}
else
{
[subscriber sendError:[NSError errorWithDomain:@"报错了" code:401 userInfo:nil]];
}
return nil;
}];
return otherSignal;
}];
}
return _otherMyRaccomand;
} //创建主线程上运行
-(RACCommand *)mainThreadCommend
{
@weakify(self);
if (!_mainThreadCommend) {
_mainThreadCommend=[[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
RACSignal *authSignal=[RACSignal empty];
@strongify(self); authSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
//可以根据要求加条件进行判断是否创建信号
if ([self.username isEqualToString:@"wjy"]) {
[subscriber sendNext:@(YES)];
[subscriber sendCompleted];
}
else
{
//错误
[subscriber sendError:[NSError errorWithDomain:@"报错了" code:1 userInfo:nil]];
}
return nil;
}];
return authSignal;
}];
}
return _mainThreadCommend;
} //特性测试
-(RACCommand *)testPropertyCommend
{
if (!_testPropertyCommend) { } return _testPropertyCommend;
} -(RACSignal *)nettestSignal
{
RACSignal *authSignal=[RACSignal empty]; authSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
LogInApi *reg = [[LogInApi alloc] initWithUsername:self.username password:@"123456"];
[reg startWithCompletionBlockWithSuccess:^(YTKBaseRequest *request) {
LoginModel *model=[[LoginModel alloc]initWithString:request.responseString error:nil];
[subscriber sendNext:model];
[subscriber sendCompleted];
} failure:^(YTKBaseRequest *request) {
[subscriber sendError:[NSError errorWithDomain:@"报错了" code:1 userInfo:nil]];
}];
return nil;
}];
return authSignal;
} -(RACSignal *)netSignal
{
RACSignal *authSignal=[RACSignal empty];
authSignal=[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"测试"];
[subscriber sendCompleted]; return nil;
}];
return authSignal;
} //RACSubject的运用
-(RACSubject *)testSubject
{
RACSubject *sub=[RACSubject subject];
[sub sendNext:@"我是RACSubject"];
return sub;
}
运用的内容:
[self loadPage]; @weakify(self); //把左边的属性跟右边信号signal的sendNext值绑定 distinctUntilChanged为了当值相同时不执行
RAC(self,username)=[self.userNameText.rac_textSignal distinctUntilChanged]; //监听username是否有变化,有变化就会执行subscribeNext 这个属性要支持KVO 可变数组就不可以
[RACObserve(self, username) subscribeNext:^(NSString *x) {
NSLog(@"你当前输入的值为:%@",x);
}]; //UIAlertView跟RAC结合
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"" message:@"Alert" delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[[alertView rac_buttonClickedSignal] subscribeNext:^(NSNumber *indexNumber) {
if ([indexNumber intValue] == 1) {
NSLog(@"你点了确定");
} else {
NSLog(@"你点了取消");
}
}];
[alertView show]; //Button响应事件
[[self.loginButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(UIButton *x) {
@strongify(self);
[[self testSignal] subscribeNext:^(id x) {
NSLog(@"当前执行值为:%@",x);
}];
}]; //Button用命令式运行 switchToLatest处理信号中传递的参数为信号
self.racCommendButton.rac_command=[self testCommend]; //这个绑定要放在executionSignals执行前面 否则只会有一个执行完成会响应
[self.racCommendButton.rac_command.executing subscribeNext:^(id x) {
if ([x boolValue]) {
NSLog(@"rac_command正在执行中");
}
else
{
NSLog(@"rac_command执行完成");
}
}]; //dematerialize处理可以响应处理跟完成的内容
[self.racCommendButton.rac_command.executionSignals subscribeNext:^(RACSignal *execution) {
[[[execution dematerialize] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(id x) {
NSLog(@"提示内容:%@",x);
} error:^(NSError *error) {
NSLog(@"racCommend出错了");
}];
}]; //关于otherMyRaccomand的方式
self.errCommendButton.rac_command=self.otherMyRaccomand; [self.otherMyRaccomand.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"完成了 %@",x);
}]; //这边要注意是在errors 里面执行subscribeNext 不是执行subscribeError
[self.otherMyRaccomand.errors subscribeNext:^(NSError *error) {
NSLog(@"出错了 %@",error);
}]; //主线程上操作
self.mainThreadButton.rac_command=self.mainThreadCommend;
[[[self.mainThreadCommend.executionSignals.switchToLatest map:^id(id value) {
if ([value boolValue]) {
return @"跳浪帅";
}
else
{
return @"出太阳好么";
}
}] deliverOn:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSString *str) {
UIAlertView *alert=[[UIAlertView alloc]initWithTitle:@"我是弹出窗" message:[NSString stringWithFormat:@"%@",str] delegate:nil cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
[alert show];
}]; //网络请求
//Button响应事件
[[self.netWorkButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(id x) {
[[[self netSignal] flattenMap:^RACStream *(id value) {
return [RACReturnSignal return:[NSString stringWithFormat:@"%@ 我已经被改变了成为另外一个信号",value]];
}]
subscribeNext:^(id x) {
NSLog(@"输出的内容:%@",x);
}]; }];
三:双向绑定
RACChannelTerminal *channelA = RACChannelTo(self, valueA);
RACChannelTerminal *channelB = RACChannelTo(self, valueB);
[[channelA map:^id(NSString *value) {
if ([value isEqualToString:@"西"]) {
return @"东";
}
return value;
}] subscribe:channelB];
[[channelB map:^id(NSString *value) {
if ([value isEqualToString:@"左"]) {
return @"右";
}
return value;
}] subscribe:channelA];
[[RACObserve(self, valueA) filter:^BOOL(id value) {
return value ? YES : NO;
}] subscribeNext:^(NSString* x) {
NSLog(@"你向%@", x);
}];
[[RACObserve(self, valueB) filter:^BOOL(id value) {
return value ? YES : NO;
}] subscribeNext:^(NSString* x) {
NSLog(@"他向%@", x);
}];
self.valueA = @"西";
self.valueB = @"左";
2015-08-15 20:14:46.544 Test[2440:99901] 你向西
2015-08-15 20:14:46.544 Test[2440:99901] 他向东
2015-08-15 20:14:46.545 Test[2440:99901] 他向左
2015-08-15 20:14:46.545 Test[2440:99901] 你向右
一个关于左右列表相互影响滚动的双向绑定实例:
RACChannelTerminal *leftChannel = RACChannelTo(self.leftTableView, contentOffset);
RACChannelTerminal *rightChannel = RACChannelTo(self.rightTableView, contentOffset);
[[rightChannel map:^id(NSValue *offset) {
CGPoint point = offset.CGPointValue;
point.y /= 10;
return [NSValue valueWithCGPoint:point];
}] subscribe:leftChannel];
[[leftChannel map:^id(NSValue *offset) {
CGPoint point = offset.CGPointValue;
point.y *= 10;
return [NSValue valueWithCGPoint:point];
}] subscribe:rightChannel];
一个输入内容自动计算:
RACChannelTerminal *characterRemainingTerminal = RACChannelTo(_loginButton, titleLabel.text); [[self.userNameText.rac_textSignal map:^id(NSString *text) {
return [@(100 - (NSInteger)text.length) stringValue];
}] subscribe:characterRemainingTerminal];
二个输入框相互影响:
RACChannelTerminal *textField1Channel = [view.textField1 rac_newTextChannel];
RACChannelTerminal *textField2Channel = [view.textField2 rac_newTextChannel]; [textField1Channel subscribe:textField2Channel];
[textField2Channel subscribe:textField1Channel];
四:重点类的说明
1: streams
streams代表任意的值,其值会随着事件发⽣变化,由RACStream类表⽰。值可能⻢上可⽤,或者
在将来某⼀段时间可⽤,但必须按顺序获取,也就是说,在获取到第⼀个值之前,是不可能获取到
第⼆个值。
streams 是⼀个构造因果关系的结构(Monad), 要了解这个概念可以看这篇⽂章:http://
blog.csdn.net/ensoo/article/details/7506872。它可以实现在基本的初始值上进⾏复杂的操作运算
(filter,map,reducet等)。
streams不会被经常使⽤,⼤多情况下表现为signal和sequences,即Signal和Sequence是由stream
继承。
2:Signals
Signals由RACSignal类表⽰。
Signals⼀般表⽰将来被传递的数据。当信号接收到数据时,值就会通过signal被发送出去,它推送
数据给订阅者。⽤户必须订阅信号才能获取到它的值。
Signals发送3种不同类型的事件给它的订阅者:
1)next:next事件是stream提供了⼀个新值。⽽RACStream类的⽅法也只能在这个值上进⾏操作运
算。和cocoa的集合不同的是,它可以包含⼀个nil值。
2)error:error事件表⽰在信号完成之前发⽣了错误。这个事件包含了⼀个NSError类型的错误。这
个值不会在RACStream类⾥存储。
3)completed事件:表⽰信号已经成功地完成了。这个值也不会在RACStream类⾥存储。
3:Subscription
subscriber(订阅者)表⽰等待或者能够等待信号发送事件的任意对象。在框架中使⽤
RACSubscriber 协议表⽰,也即任意实现了RACSubscriber协议的对象都可以是订阅者。
可以通过调⽤ -subscribeNext:error:completed:⽅法来创建订阅。RACStream 和 RACSignal类的⼤
多操作也会⾃⼰创建订阅。
订阅会对Signals对象引⽤计数加1,当信号发送错误或者完成事件后,会⾃动被处理,不需要⽤户
关⼼内存管理。当然,⽤户也可以⼿动处理。
4: Subjects
subject由RACSubject类表⽰,它是可以⼿动控制的信号。
subject可以想成是signal的变体,就像NSMutableArray相对于NSArray⼀样。它们是⾮RAC的代码
和RAC代码之间的桥梁,因此,⾮常有⽤。
有些subject提供了额外的功能。特别地,RACReplaySubject⽤于缓存事件,将来有订阅者时,可以
将缓存的数据发送给订阅者,⽐如,当请求⼀个⺴络时,服务器数据已经返回,但其它的数据还没
有准备好,这时,先要将⺴络请求结果缓存,等其它数据准备好时,再订阅,⽽不⾄于⺴络请求数
据丢失。
5:Commands
command由RACCommand 类表⽰,它创建并订阅响应action的信号。
通常command是由UI触发的,像⼀个按钮被点击时。当command被触发时,控件会⾃动被禁⽤。
6:Connections
connection由RACMulticastConnection类表⽰。它是在任意数量的订阅者之间共享订阅。
默认情况下,信号没有订阅时,是冷信号,也即当有订阅者被添加进来后,信号才会开始⼯作,也
即变为热信号。
这也是期望的⾏为,对于每个订阅,数据值都会被延迟重新计算。否则,如果信号有副作⽤或者任
务开销很⼤时,可能就会产⽣问题。
conection通过RACSignal的publish或者multicast⽅法创建, ⼀旦连接,信号就变为热信息,订阅会
保持激活状态直到所有的订阅连接被取消。
7:Sequences
sequence由RACSequence 类表⽰。
sequence是⼀种集合,类似于NSArray。和数组不同,为了改进性能,在sequence⾥的值会延迟计
算,也即,只有需要输出值时,结果才会被计算。sequences和cocoa⾥的集合⼀样,也不能包含nil
值。
对于⼤多数据的集合类,RAC添加了-rac_sequence ⽅法来⽣成RACSequence类进⾏操作。
8:Disposables
RACDisposable 类⽤于取消订阅或者清理资源。
Disposables⼤多情况下⽤于取消信号的订阅 。
9:Schedulers
scheduler由RACScheduler类表⽰,它是信号执⾏任务时所在的队列(queue)或者信号执⾏完后将
结果放到队列⾥执⾏,可以认为就是gcd⾥的queues。
scheduler⽀持取消操作,⽽且它总是串⾏地执⾏任务。这有利于避免死锁。
RACScheduler有时候也类似NSOperationQueue,但它不允许任务间相互依赖。
10:Value types
RAC提供了以下类⽤于表⽰各种值:
1)RACTuple: ⼀个⽐较少数量值,⼤⼩固定的,可以包含nil的集合。⼀般⽤于表⽰多个streams
的聚合值。
2)RACUnit :⼀个单例的空值,表⽰stream不包含有意义的值。
3)RACEvent : 代表任意的信号事件。
11:Asynchronous Backtraces
由于基于RAC的代码经常和异步相关,因此,为了更⽅便调试,RAC⽀持捕获当前的asynchronous
backtraces(异步调⽤栈)。
五:副作用及消除副作用
__block int aNumber = 0; // Signal that will have the side effect of incrementing `aNumber` block
// variable for each subscription before sending it.
RACSignal *aSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
aNumber++;
[subscriber sendNext:@(aNumber)];
[subscriber sendCompleted];
return nil;
}]; // This will print "subscriber one: 1"
[aSignal subscribeNext:^(id x) {
NSLog(@"subscriber one: %@", x);
}]; // This will print "subscriber two: 2"
[aSignal subscribeNext:^(id x) {
NSLog(@"subscriber two: %@", x);
}];
上面这样就会产生副作用,第一个跟第二个的值会不一样,被累加;采用RACMulticastConnection来消除
__block int aNumber = 0; // Signal that will have the side effect of incrementing `aNumber` block
// variable for each subscription before sending it.
RACSignal *aSignal = [RACSignal createSignal:^ RACDisposable * (id<RACSubscriber> subscriber) {
aNumber++;
[subscriber sendNext:@(aNumber)];
[subscriber sendCompleted];
return nil;
}]; RACMulticastConnection *connection = [aSignal multicast:[RACReplaySubject subject]];
[connection connect]; // This will print "subscriber one: 1"
[connection.signal subscribeNext:^(id x) {
NSLog(@"subscriber one: %@", x);
}]; // This will print "subscriber two: 1"
[connection.signal subscribeNext:^(id x) {
NSLog(@"subscriber two: %@", x);
}];
这样两次都只返回相同的内容,为1;其同要注意是调用connection.signal;下面是官网的一个网络请求实例:
RACSignal *networkRequest = [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
AFHTTPRequestOperation *operation = [client
HTTPRequestOperationWithRequest:request
success:^(AFHTTPRequestOperation *operation, id response) {
[subscriber sendNext:response];
[subscriber sendCompleted];
}
failure:^(AFHTTPRequestOperation *operation, NSError *error) {
[subscriber sendError:error];
}]; [client enqueueHTTPRequestOperation:operation];
return [RACDisposable disposableWithBlock:^{
[operation cancel];
}];
}]; // Starts a single request, no matter how many subscriptions `connection.signal`
// gets. This is equivalent to the -replay operator, or similar to
// +startEagerlyWithScheduler:block:.
RACMulticastConnection *connection = [networkRequest multicast:[RACReplaySubject subject]];
[connection connect]; [connection.signal subscribeNext:^(id response) {
NSLog(@"subscriber one: %@", response);
}]; [connection.signal subscribeNext:^(id response) {
NSLog(@"subscriber two: %@", response);
}];
这两个订阅者接收到了同样的一个请求的内容。
六:工作原理
因为代码中已经有相应的注解,所以文字说明就比较少,若有不明白可以留言;
ReactiveCocoa学习总结的更多相关文章
- ReactiveCocoa学习资料
ReactiveCocoa 学习资料: ReactiveCocoa入门教程:第一部分 http://www.cocoachina.com/ios/20150123/10994.html Reactiv ...
- ReactiveCocoa 学习资料
之前就有听说,感觉很强大,ReactiveCocoa更加被Mattt Thompson大神称为开启一个新Objective-C纪元.所以觉得非常有学习的必要了. 一些很好的学习资料: Reactive ...
- iOS开发ReactiveCocoa学习笔记(六)
RAC操作方法三. demo地址:https://github.com/SummerHH/ReactiveCocoa.git doNext deliverOn timeout interval del ...
- iOS开发ReactiveCocoa学习笔记(一)
学习 RAC 我们首先要了解 RAC 都有哪些类 RACSignal RACSubject RACSequence RACMulticastConnection RACCommand 在学习的时候写了 ...
- ReactiveCocoa学习
ReactiveCocoa常见类 6.1RACSiganl:信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据. 注意: 信号类(RACSiganl),只是表示当数据 ...
- CocoaPods ReactiveCocoa 学习实践一 之 配置环境
1.安装CocoaPods 1.00.参考 CocoaPods 文档 1.01.是否已安装 which pod 1.1.升级gem命令 sudo gem update --system 1.2.切换C ...
- ReactiveCocoa学习总结(1)
1. 它是什么? 官方解释: [RACSignal] is a push-driven stream with a focus on asynchronous event delivery throu ...
- iOS开发ReactiveCocoa学习笔记(五)
ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git filter ignore ignoreValu ...
- iOS开发ReactiveCocoa学习笔记(四)
ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git 1.1 ReactiveCocoa操作须知 所有 ...
随机推荐
- 第2/24周 页_SQL Server 中数据存储的基本单位
上周通过探讨SQL Server如何执行一个查询奠定了基础.我也在那里提到页是8kb的缓存.今天我们对页进行进一步集中探讨,从性能调优角度挖掘出更多的细节. 页是SQL Server的基础,在SQL ...
- 【Swift学习】Swift编程之旅---类和结构体(十三)
与其他编程语言所不同的是,Swift 并不要求你为自定义类和结构去创建独立的接口和实现文件.你所要做的是在一个单一文件中定义一个类或者结构体,系统将会自动生成面向其它代码的外部接口. 注意:通常一个类 ...
- OpenJudge2728:摘花生 解题报告
2728:摘花生 总时间限制: 1000ms 内存限制: 65536kB 描述 Hello Kitty 想摘点花生送给她喜欢的米老鼠.她来到一片有网格状道路的矩形花生地(如下图),从西北角进去,东南 ...
- html和js基础功能代码备份
1)贴图:<img src="图片地址">2)加入连接:<a href="所要连接的相关地址">写上你想写的字</a> 3) ...
- asp.net中Ajax控件的用途(一)
1,UpdatePanel控件,用户更新部分内容,示例 放入一个Label和一个Button,单击按钮,label显示当前时间. 2,ScriptManagerProxy控件,每个页面只能有一个Scr ...
- PHP多种形式发送邮件
1. 使用 mail() 函数 没什么好讲的,就是使用系统自带的smtp系统来发送,一般是使用sendmail来发.这个按照各个系统不同而定.使用参考手册. 2. 使用管道的形式 昨天刚测试成功,使用 ...
- MySql基础整理
http://hovertree.com/menu/mysql/ use abccs;select * from mytable2 limit 3,4;call sp_name1(1,@nn);sel ...
- 说说这篇「我为什么从python转向go
作者 CMGS2015.05.17 15:47* 写了7891字,被143人关注,获得了97个喜欢 说说这篇「我为什么从python转向go」 字数3748 阅读24227 评论21 喜欢81 恩看了 ...
- 说说Java的内存
首先,我们来看一段程序内存溢出的代码: import java.util.ArrayList; import java.util.List; public class TestMemoryLeak { ...
- 【Qt】2.3 使用Qt设计师来创建对话框
安装完Qt OpenSource之后,在开始菜单目录下会有这几个东西. 其中[Designer]是用来设计窗口界面的程序.所以现在可以使用它来设计一个对话框.在[Qt Creator]中,[设计]这一 ...