ReactiveCocoa学习总结
最近一直断断续续学习关于ReactiveCocoa的知识内容,对于它的一些基础内容将通过本文进行一个总结,主要是一些入门知识
一:RACSignal一些运用
1 2 3 | @ interface RACSignalTestViewController () @property(nonatomic,strong)RACSignal *mySignal,*secondSingl; @end |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | -(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的内容;将用于接下来的一些操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //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):跳过几个信号,不接受。 |
运行结果:
1 2 3 | 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:一些组合的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | //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); }]; |
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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:其它一些操作:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | //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节流:当某个信号发送比较频繁时,可以使用节流,在某一段时间不发送信号内容,过了一段时间获取信号的最新内容发出。 |
运行结果:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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的一些知识
1 2 3 4 5 6 7 | @ 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 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | //布局 -( 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); }]; } } |
一些定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 | //创建一个信号代码 -(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; } |
运用的内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | [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); }]; }]; |
三:双向绑定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | 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 = @"左" ; |
1 2 3 4 | 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] 你向右 |
一个关于左右列表相互影响滚动的双向绑定实例:
1 2 3 4 5 6 7 8 9 10 11 12 | 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]; |
一个输入内容自动计算:
1 2 3 4 5 | RACChannelTerminal *characterRemainingTerminal = RACChannelTo(_loginButton, titleLabel.text); [[self.userNameText.rac_textSignal map:^id(NSString *text) { return [@(100 - (NSInteger)text.length) stringValue]; }] subscribe:characterRemainingTerminal]; |
二个输入框相互影响:
1 2 3 4 5 | RACChannelTerminal *textField1Channel = [view.textField1 rac_newTextChannel]; RACChannelTerminal *textField2Channel = [view.textField2 rac_newTextChannel]; [textField1Channel subscribe:textField2Channel]; [textField2Channel subscribe:textField1Channel]; |
四:重点类的说明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | 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(异步调⽤栈)。 |
五:副作用及消除副作用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | __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来消除
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | __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;下面是官网的一个网络请求实例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | 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); }]; |
这两个订阅者接收到了同样的一个请求的内容。
六:工作原理
因为代码中已经有相应的注解,所以文字说明就比较少,若有不明白可以留言;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· .NET周刊【3月第1期 2025-03-02】
· [AI/GPT/综述] AI Agent的设计模式综述
2012-02-02 学习MVC第二个实例登录代码(Model数据验证)
2012-02-02 学习MVC第一个增删修功能的本记