使用ReactiveCocoa实现iOS平台响应式编程

前端之家收集整理的这篇文章主要介绍了使用ReactiveCocoa实现iOS平台响应式编程前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

@H_404_1@原文地址:http://blog.csdn.net/xdrt81y/article/details/30624469

@H_404_1@

@H_404_1@使用ReactiveCocoa实现iOS平台响应式编程

@H_404_1@ReactiveCocoa和响应式编程

@H_404_1@在说ReactiveCocoa之前,先要介绍一下FRP(Functional Reactive Programming,响应式编程),在维基百科中有这样一个例子介绍:

@H_404_1@在命令式编程环境中,a = b + c 表示将表达式的结果赋给a,而之后改变b或c的值不会影响a。但在响应式编程中,a的值会随着b或c的更新而更新。

@H_404_1@Excel就是响应式编程的一个例子。单元格可以包含字面值或类似”=B1+C1″的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化 。

@H_404_1@而ReactiveCocoa简称RAC,就是基于响应式编程思想的Objective-C实践,它是Github的一个开源项目,你可以在这里找到它。

@H_404_1@关于FRP和ReactiveCocoa可以去看leezhong的这篇blog,图文并茂,讲的很好。

@H_404_1@ReactiveCocoa框架概览

@H_404_1@先来看一下leezhong再博文中提到的比喻,让你对有个ReactiveCocoa很好的理解:

@H_404_1@可以把信号想象成水龙头,只不过里面不是水,而是玻璃球(value),直径跟水管的内径一样,这样就能保证玻璃球是依次排列,不会出现并排的情况(数据都是线性处理的,不会出现并发情况)。水龙头的开关默认是关的,除非有了接收方(subscriber),才会打开。这样只要有新的玻璃球进来,就会自动传送给接收方。可以在水龙头上加一个过滤嘴(filter),不符合的不让通过,也可以加一个改动装置,把球改变成符合自己的需求(map)。也可以把多个水龙头合并成一个新的水龙头(combineLatest:reduce:),这样只要其中的一个水龙头有玻璃球出来,这个新合并的水龙头就会得到这个球。

@H_404_1@下面我来逐一介绍ReactiveCocoa框架的每个组件

@H_404_1@Streams

@H_404_1@Streams表现为RACStream类,可以看做是水管里面流动的一系列玻璃球,它们有顺序的依次通过,在第一个玻璃球没有到达之前,你没法获得第二个玻璃球。
RACStream描述的就是这种线性流动玻璃球的形态,比较抽象,它本身的使用意义并不很大,一般会以signals或者sequences等这些更高层次的表现形态代替。

@H_404_1@

@H_404_1@Signals

@H_404_1@Signals表现为RACSignal类,就是前面提到水龙头,ReactiveCocoa的核心概念就是Signal,它一般表示未来要到达的值,想象玻璃球一个个从水龙头里出来,只有了接收方(subscriber)才能获取到这些玻璃球(value)。

@H_404_1@Signal会发送下面三种事件给它的接受方(subscriber),想象成水龙头有个指示灯来汇报它的工作状态,接受方通过-subscribeNext:error:completed:对不同事件作出相应反应

  • @H_404_1@next从水龙头里流出的新玻璃球(value)
  • @H_404_1@error获取新的玻璃球发生了错误,一般要发送一个NSError对象,表明哪里错了
  • @H_404_1@completed全部玻璃球已经顺利抵达,没有更多的玻璃球加入了

@H_404_1@一个生命周期的Signal可以发送任意多个“next”事件,和一个“error”或者“completed”事件(当然“error”和“completed”只可能出现一种)

@H_404_1@Subjects

@H_404_1@subjects表现为RACSubject类,可以认为是“可变的(mutable)”信号/自定义信号,它是嫁接非RAC代码到Signals世界的桥梁,很有用。嗯。。。 这样讲还是很抽象,举个例子吧:

@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@RACSubject *letters=[RACSubjectsubject];
@H_404_1@RACSignal *signal=[letteRSSendNext:@"a"];
@H_404_1@

@H_404_1@可以看到@"a"只是一个NSString对象,要想在水管里顺利流动,就要借RACSubject的力。

@H_404_1@Commands

@H_404_1@command表现为RACCommand类,偷个懒直接举个例子吧,比如一个简单的注册界面:

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@14
@H_404_1@15
@H_404_1@16
@H_404_1@17
@H_404_1@18
@H_404_1@19
@H_404_1@20
@H_404_1@21
@H_404_1@RACSignal *formValid=[RACSignal
@H_404_1@combineLatest:@[
@H_404_1@self.userNameField.rac_textSignal,
@H_404_1@self.emailField.rac_textSignal,
@H_404_1@]
@H_404_1@reduce:^(NSString *userName,NSString *email){
@H_404_1@return@(userName.length>0
@H_404_1@&&email.length>0);
@H_404_1@}];
@H_404_1@
@H_404_1@RACCommand *createAccountCommand=[RACCommandcommandWithCanExecuteSignal:formValid];
@H_404_1@RACSignal *networkResults=[[[createAccountCommand
@H_404_1@addSignalBlock:^RACSignal *(idvalue){
@H_404_1@//... 网络交互代码
@H_404_1@}]
@H_404_1@switchToLatest]
@H_404_1@deliverOn:[RACSchedulermainThreadScheduler]];
@H_404_1@
@H_404_1@// 绑定创建按钮的 UI state 和点击事件
@H_404_1@[[self.createButtonrac_signalForControlEvents:UIControlEventTouchUpInside]executeCommand:createAccountCommand];
@H_404_1@

@H_404_1@Sequences

@H_404_1@sequence表现为RACSequence类,可以简单看做是RAC世界的NSArray,RAC增加-rac_sequence方法,可以使诸如NSArray这些集合类(collection classes)直接转换为RACSequence来使用。

@H_404_1@Schedulers

@H_404_1@scheduler表现为RACScheduler类,类似于GCD,but schedulers support cancellationbut schedulers support cancellation,and always execute serially.

@H_404_1@ReactiveCocoa的简单使用

@H_404_1@实践出真知,下面就举一些简单的例子,一起看看RAC的使用

@H_404_1@Subscription

@H_404_1@接收-subscribeNext:-subscribeError:-subscribeCompleted:

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@RACSignal *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence.signal;
@H_404_1@
@H_404_1@// 依次输出 A B C D…
@H_404_1@[letteRSSubscribeNext:^(NSString *x){
@H_404_1@NSLog(@"%@",x);
@H_404_1@}];
@H_404_1@

@H_404_1@Injecting effects

@H_404_1@注入效果-doNext:-doError:-doCompleted:,看下面注释应该就明白了:

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@14
@H_404_1@15
@H_404_1@16
@H_404_1@17
@H_404_1@18
@H_404_1@19
@H_404_1@20
@H_404_1@__blockunsignedsubscriptions=0;
@H_404_1@
@H_404_1@RACSignal *loggingSignal=[RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber>subscriber){
@H_404_1@subscriptions++;
@H_404_1@[subscribersendCompleted];
@H_404_1@returnnil;
@H_404_1@}];
@H_404_1@
@H_404_1@// 不会输出任何东西
@H_404_1@loggingSignal=[loggingSignaldoCompleted:^{
@H_404_1@NSLog(@"about to complete subscription %u",subscriptions);
@H_404_1@}];
@H_404_1@
@H_404_1@// 输出:
@H_404_1@// about to complete subscription 1
@H_404_1@// subscription 1
@H_404_1@[loggingSignalsubscribeCompleted:^{
@H_404_1@NSLog(@"subscription %u",subscriptions);
@H_404_1@}];
@H_404_1@

@H_404_1@Mapping

@H_404_1@-map:映射,可以看做对玻璃球的变换、重新组装

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@RACSequence *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence;
@H_404_1@
@H_404_1@// Contains: AA BB CC DD EE FF GG HH II
@H_404_1@RACSequence *mapped=[lettersmap:^(NSString *value){
@H_404_1@return[valuestringByAppendingString:value];
@H_404_1@}];
@H_404_1@

@H_404_1@Filtering

@H_404_1@-filter:过滤,不符合要求的玻璃球不允许通过

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@RACSequence *numbers=[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence;
@H_404_1@
@H_404_1@// Contains: 2 4 6 8
@H_404_1@RACSequence *filtered=[numbersfilter:^BOOL(NSString *value){
@H_404_1@return(value.intValue%2)==0;
@H_404_1@}];
@H_404_1@

@H_404_1@Concatenating

@H_404_1@-concat:把一个水管拼接到另一个水管之后

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@RACSequence *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence;
@H_404_1@RACSequence *numbers=[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence;
@H_404_1@
@H_404_1@// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
@H_404_1@RACSequence *concatenated=[lettersconcat:numbers];
@H_404_1@

@H_404_1@Flattening

@H_404_1@-flatten:

@H_404_1@Sequences are concatenated

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@RACSequence *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence;
@H_404_1@RACSequence *numbers=[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence;
@H_404_1@RACSequence *sequenceOfSequences=@[letters,numbers].rac_sequence;
@H_404_1@
@H_404_1@// Contains: A B C D E F G H I 1 2 3 4 5 6 7 8 9
@H_404_1@RACSequence *flattened=[sequenceOfSequencesflatten];
@H_404_1@

@H_404_1@Signals are merged (merge可以理解成把几个水管的龙头合并成一个,哪个水管中的玻璃球哪个先到先吐哪个玻璃球)

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@14
@H_404_1@15
@H_404_1@16
@H_404_1@17
@H_404_1@18
@H_404_1@19
@H_404_1@20
@H_404_1@21
@H_404_1@22
@H_404_1@RACSubject *letters=[RACSubjectsubject];
@H_404_1@RACSubject *numbers=[RACSubjectsubject];
@H_404_1@RACSignal *signalOfSignals=[RACSignalcreateSignal:^RACDisposable *(id<RACSubscriber>subscriber){
@H_404_1@[subscribersendNext:letters];
@H_404_1@[subscribersendNext:numbers];
@H_404_1@[subscribersendCompleted];
@H_404_1@returnnil;
@H_404_1@}];
@H_404_1@
@H_404_1@RACSignal *flattened=[signalOfSignalsflatten];
@H_404_1@
@H_404_1@// Outputs: A 1 B C 2
@H_404_1@[flattenedsubscribeNext:^(NSString *x){
@H_404_1@NSLog(@"%@",x);
@H_404_1@}];
@H_404_1@
@H_404_1@[letteRSSendNext:@"A"];
@H_404_1@[numbeRSSendNext:@"1"];
@H_404_1@[letteRSSendNext:@"B"];
@H_404_1@[letteRSSendNext:@"C"];
@H_404_1@[numbeRSSendNext:@"2"];
@H_404_1@

@H_404_1@Mapping and flattening

@H_404_1@-flattenMap:先 map 再 flatten

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@14
@H_404_1@15
@H_404_1@16
@H_404_1@17
@H_404_1@18
@H_404_1@19
@H_404_1@20
@H_404_1@21
@H_404_1@22
@H_404_1@23
@H_404_1@24
@H_404_1@25
@H_404_1@26
@H_404_1@27
@H_404_1@28
@H_404_1@29
@H_404_1@30
@H_404_1@RACSequence *numbers=[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence;
@H_404_1@
@H_404_1@// Contains: 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9
@H_404_1@RACSequence *extended=[numbersflattenMap:^(NSString *num){
@H_404_1@return@[num,num].rac_sequence;
@H_404_1@}];
@H_404_1@
@H_404_1@// Contains: 1_ 3_ 5_ 7_ 9_
@H_404_1@RACSequence *edited=[numbersflattenMap:^(NSString *num){
@H_404_1@if(num.intValue%2==0){
@H_404_1@return[RACSequenceempty];
@H_404_1@}else{
@H_404_1@NSString *newNum=[numstringByAppendingString:@"_"];
@H_404_1@return[RACSequencereturn:newNum];
@H_404_1@}
@H_404_1@}];
@H_404_1@
@H_404_1@
@H_404_1@
@H_404_1@
@H_404_1@RACSignal *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence.signal;
@H_404_1@
@H_404_1@[[letters
@H_404_1@flattenMap:^(NSString *letter){
@H_404_1@return[databasesaveEntriesForLetter:letter];
@H_404_1@}]
@H_404_1@subscribeCompleted:^{
@H_404_1@NSLog(@"All database entries saved successfully.");
@H_404_1@}];
@H_404_1@

@H_404_1@Sequencing

@H_404_1@-then:

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@RACSignal *letters=[@"A B C D E F G H I"componentsSeparatedByString:@" "].rac_sequence.signal;
@H_404_1@
@H_404_1@// 新水龙头只包含: 1 2 3 4 5 6 7 8 9
@H_404_1@//
@H_404_1@// 但当有接收时,仍会执行旧水龙头doNext的内容,所以也会输出 A B C D E F G H I
@H_404_1@RACSignal *sequenced=[[letters
@H_404_1@doNext:^(NSString *letter){
@H_404_1@NSLog(@"%@",letter);
@H_404_1@}]
@H_404_1@then:^{
@H_404_1@return[@"1 2 3 4 5 6 7 8 9"componentsSeparatedByString:@" "].rac_sequence.signal;
@H_404_1@}];
@H_404_1@

@H_404_1@Merging

@H_404_1@+merge:前面在flatten中提到的水龙头的合并

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@14
@H_404_1@15
@H_404_1@RACSubject *letters=[RACSubjectsubject];
@H_404_1@RACSubject *numbers=[RACSubjectsubject];
@H_404_1@RACSignal *merged=[RACSignalmerge:@[letters,numbers]];
@H_404_1@
@H_404_1@// Outputs: A 1 B C 2
@H_404_1@[mergedsubscribeNext:^(NSString *x){
@H_404_1@NSLog(@"%@",x);
@H_404_1@}];
@H_404_1@
@H_404_1@[letteRSSendNext:@"A"];
@H_404_1@[numbeRSSendNext:@"1"];
@H_404_1@[letteRSSendNext:@"B"];
@H_404_1@[letteRSSendNext:@"C"];
@H_404_1@[numbeRSSendNext:@"2"];
@H_404_1@

@H_404_1@Combining latest values

@H_404_1@+combineLatest:任何时刻取每个水龙头吐出的最新的那个玻璃球

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@14
@H_404_1@15
@H_404_1@16
@H_404_1@17
@H_404_1@18
@H_404_1@19
@H_404_1@20
@H_404_1@RACSubject *letters=[RACSubjectsubject];
@H_404_1@RACSubject *numbers=[RACSubjectsubject];
@H_404_1@RACSignal *combined=[RACSignal
@H_404_1@combineLatest:@[letters,numbers]
@H_404_1@reduce:^(NSString *letter,NSString *number){
@H_404_1@return[letterstringByAppendingString:number];
@H_404_1@}];
@H_404_1@
@H_404_1@// Outputs: B1 B2 C2 C3
@H_404_1@[combinedsubscribeNext:^(idx){
@H_404_1@NSLog(@"%@",x);
@H_404_1@}];
@H_404_1@
@H_404_1@[letteRSSendNext:@"A"];
@H_404_1@[letteRSSendNext:@"B"];
@H_404_1@[numbeRSSendNext:@"1"];
@H_404_1@[numbeRSSendNext:@"2"];
@H_404_1@[letteRSSendNext:@"C"];
@H_404_1@[numbeRSSendNext:@"3"];
@H_404_1@

@H_404_1@Switching

@H_404_1@-switchToLatest:取指定的那个水龙头的吐出的最新玻璃球

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@5
@H_404_1@6
@H_404_1@7
@H_404_1@8
@H_404_1@9
@H_404_1@10
@H_404_1@11
@H_404_1@12
@H_404_1@13
@H_404_1@14
@H_404_1@15
@H_404_1@16
@H_404_1@17
@H_404_1@18
@H_404_1@19
@H_404_1@20
@H_404_1@21
@H_404_1@22
@H_404_1@23
@H_404_1@RACSubject *letters=[RACSubjectsubject];
@H_404_1@RACSubject *numbers=[RACSubjectsubject];
@H_404_1@RACSubject *signalOfSignals=[RACSubjectsubject];
@H_404_1@
@H_404_1@RACSignal *switched=[signalOfSignalsswitchToLatest];
@H_404_1@
@H_404_1@// Outputs: A B 1 D
@H_404_1@[switchedsubscribeNext:^(NSString *x){
@H_404_1@NSLog(@"%@",x);
@H_404_1@}];
@H_404_1@
@H_404_1@[signalOfSignalssendNext:letters];
@H_404_1@[letteRSSendNext:@"A"];
@H_404_1@[letteRSSendNext:@"B"];
@H_404_1@
@H_404_1@[signalOfSignalssendNext:numbers];
@H_404_1@[letteRSSendNext:@"C"];
@H_404_1@[numbeRSSendNext:@"1"];
@H_404_1@
@H_404_1@[signalOfSignalssendNext:letters];
@H_404_1@[numbeRSSendNext:@"2"];
@H_404_1@[letteRSSendNext:@"D"];
@H_404_1@

@H_404_1@常用宏

@H_404_1@RAC 可以看作某个属性的值与一些信号的联动

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@RAC(self.submitButton.enabled)=[RACSignalcombineLatest:@[self.usernameField.rac_textSignal,self.passwordField.rac_textSignal]reduce:^id(NSString *userName,NSString *password){
@H_404_1@return@(userName.length>=6&&password.length>=6);
@H_404_1@}];
@H_404_1@

@H_404_1@RACObserve 监听属性的改变,使用block的KVO

@H_404_1@
@H_404_1@1
@H_404_1@2
@H_404_1@3
@H_404_1@4
@H_404_1@[RACObserve(self.textField,text)subscribeNext:^(NSString *newName){
@H_404_1@NSLog(@"%@",newName);
@H_404_1@}];
@H_404_1@

@H_404_1@UI Event

@H_404_1@RAC为系统UI提供了很多category,非常棒,比如UITextView、UITextField文本框的改动rac_textSignal,UIButton的的按下rac_command等等。

@H_404_1@最后

@H_404_1@有了RAC,可以不用去操心值什么时候到达什么时候改变,只需要简单的进行数据来了之后的步骤就可以了。

@H_404_1@说了这么多,在回过头去看leezhong的比喻该文最后总结的关系图,再好好梳理一下吧。我也是初学者,诚惶诚恐的呈上这篇博文,欢迎讨论,如有不正之处欢迎批评指正。

@H_404_1@参考

@H_404_1@https://github.com/ReactiveCocoa/ReactiveCocoa

https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/FrameworkOverview.md

https://github.com/ReactiveCocoa/ReactiveCocoa/blob/master/Documentation/BasicOperators.md

@H_404_1@http://vimeo.com/65637501
http://iiiyu.com/2013/09/11/learning-ios-notes-twenty-eight/
http://blog.leezhong.com/ios/2013/06/19/frp-reactivecocoa.htmlhttp://nshipster.com/reactivecocoa/

猜你在找的React相关文章