标签:ios reactivecocoa 函数式 响应式 函数响应式
ReactiveCocoa函数响应式编程
一、简介
ReactiveCocoa(其简称为RAC)是函数响应式编程框架。RAC具有函数式编程和响应式编程的特性。它主要吸取了.Net的 Reactive Extensions的设计和实现。
函数式编程 (Functional Programming)
函数式编程也可以写N篇,它是完全不同于OO的编程模式,这里主要讲一下这个框架使用到的函数式思想。
1) 高阶函数:在函数式编程中,把函数当参数来回传递,而这个,说成术语,我们把他叫做高阶函数。在oc中,blocks是被广泛使用的参数传递,它实际上是匿名函数。  
     高阶函数调用过程有点像linux命令里的pipeline(管道),一个命令调用后的输出当作另一个命令输入,多个命令之间可以串起来操作。来个例子:
| 
1 
2 
3 
4 
5 
6 
7 
8 |    
  RACSequence
 *numbers = [@"1
 2 3 4 5 6 7 8 9"componentsSeparatedByString:@"
 "].rac_sequence;    
//
 Contains: 22 44 66 88   
RACSequence
 *doubleNumber = [[numbers filter:^ BOOL(NSString*value)
 {   
     return(value.intValue
 % 2) == 0;   
}]
 
   map:^id(idvalue)
 {   
     return[value
 stringByAppendingString:value];   
}]; | 
上面的例子是数组里的值先进行过滤filter,得到偶数,然后再将结果的每个值进行map操作,调用stringByAppendingString,最终输出22 44 66 88.
2) 惰性(或延迟)求值:Sequences对象等,只有当被使用到时,才会对其求值。
关于函数编程,有兴趣的大家可以研究下haskell或者clojure,不过目前好多语言都在借用函数式的思想。
响应式编程(Functional Reactive Programming:FRP)
响应式编程是一种和事件流有关的编程模式,关注导致状态值改变的行为事件,一系列事件组成了事件流。
一系列事件是导致属性值发生变化的原因。FRP非常类似于设计模式里的观察者模式。
响应式编程是一种针对数据流和变化传递的编程模式,其执行引擎可以自动的在数据流之间传递数据的变化。比如说,在一种命令式编程语言中,a: = b + c 表示 a 是 b + c 表达式的值,但是在RP语言中,它可能意味着一个动态的数据流关系:当c或者b的值发生变化时,a的值自动的发生变化。
RP已经被证实是一种最有效的处理交互式用户界面、实时模式下的动画的开发模式,但本质上是一种基本的编程模式。现在最为热门的JavaFX脚本语言中,引入的bind就是RP的一个概念实现。
响应式编程其关键点包括:
1) 输入被视为"行为",或者说一个随时间而变化的事件流
2) 连续的、随时间而变化的值
3) 按时间排序的离散事件序列
FRP与普通的函数式编程相似,但是每个函数可以接收一个输入值的流,如果其中,一个新的输入值到达的话,这个函数将根据最新的输入值重新计算,并且产生一个新的输出。这是一种”数据流"编程模式。
二、为什么我们要用它
1,开发过程中,状态以及状态之间依赖过多,RAC更加有效率地处理事件流,而无需显式去管理状态。在OO或者过程式编程中,状态变化是最难跟踪,最头痛的事。这个也是最重要的一点。
2,减少变量的使用,由于它跟踪状态和值的变化,因此不需要再申明变量不断地观察状态和更新值。
3,提供统一的消息传递机制,将oc中的通知,action,KVO以及其它所有UIControl事件的变化都进行监控,当变化发生时,就会传递事件和值。
4,当值随着事件变换时,可以使用map,filter,reduce等函数便利地对值进行变换操作。
三、何时使用
1,处理异步或者事件驱动的数据变化
| 
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 | staticvoid*ObservationContext
 = &ObservationContext;-
 (void)viewDidLoad
 {   
[superviewDidLoad];   
[LoginManager.sharedManager
 addObserver:selfforKeyPath:@"loggingIn"options:NSKeyValueObservingOptionInitialcontext:&ObservationContext];   
[NSNotificationCenter.defaultCenter
 addObserver:selfselector:@selector(loggedOut:)
 name:UserDidLogOutNotification object:LoginManager.sharedManager];   
[self.usernameTextField
 addTarget:selfaction:@selector(updateLogInButton)
 forControlEvents:UIControlEventEditingChanged];   
[self.passwordTextField
 addTarget:selfaction:@selector(updateLogInButton)
 forControlEvents:UIControlEventEditingChanged];   
[self.logInButton
 addTarget:selfaction:@selector(logInPressed:)
 forControlEvents:UIControlEventTouchUpInside];}-
 (void)dealloc
 {   
[LoginManager.sharedManager
 removeObserver:selfforKeyPath:@"loggingIn"context:ObservationContext];   
[NSNotificationCenter.defaultCenter
 removeObserver:self];}-
 (void)updateLogInButton
 {   
BOOLtextFieldsNonEmpty
 = self.usernameTextField.text.length
 > 0 && self.passwordTextField.text.length
 > 0;   
BOOLreadyToLogIn
 = !LoginManager.sharedManager.isLoggingIn && !self.loggedIn;   
self.logInButton.enabled
 = textFieldsNonEmpty && readyToLogIn;}-
 (IBAction)logInPressed:(UIButton
 *)sender {   
[[LoginManager
 sharedManager]   
     logInWithUsername:self.usernameTextField.text   
     password:self.passwordTextField.text   
     success:^{   
         self.loggedIn
 = YES;   
     }
 failure:^(NSError*error)
 {   
         [selfpresentError:error];   
     }];}-
 (void)loggedOut:(NSNotification*)notification
 {   
self.loggedIn
 = NO;}-
 (void)observeValueForKeyPath:(NSString*)keyPath
 ofObject:(id)object
 change:(NSDictionary*)change
 context:(void*)context
 {   
if(context
 == ObservationContext) {   
     [selfupdateLogInButton];   
}
else{   
     [superobserveValueForKeyPath:keyPath
 ofObject:object change:change context:context];   
}}//
 RAC实现:-
 (void)viewDidLoad
 {   
[superviewDidLoad];   
@weakify(self);   
RAC(self.logInButton,
 enabled) = [RACSignal   
     combineLatest:@[   
         self.usernameTextField.rac_textSignal,   
         self.passwordTextField.rac_textSignal,   
         RACObserve(LoginManager.sharedManager,
 loggingIn),   
         RACObserve(self,
 loggedIn)   
     ]
 reduce:^(NSString*username,
NSString*password,
NSNumber*loggingIn,
NSNumber*loggedIn)
 {   
         return@(username.length
 > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue);   
     }];   
[[self.logInButton
 rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(UIButton *sender) {   
     @strongify(self);   
     RACSignal
 *loginSignal = [LoginManager.sharedManager   
         logInWithUsername:self.usernameTextField.text   
         password:self.passwordTextField.text];   
         [loginSignal
 subscribeError:^(NSError*error)
 {   
             @strongify(self);   
             [selfpresentError:error];   
         }
 completed:^{   
             @strongify(self);   
             self.loggedIn
 = YES;   
         }];   
}];   
RAC(self,
 loggedIn) = [[NSNotificationCenter.defaultCenter   
     rac_addObserverForName:UserDidLogOutNotification
 object:nil]   
     mapReplace:@NO];} | 
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 | [client
 logInWithSuccess:^{   
[client
 loadCachedMessagesWithSuccess:^(NSArray*messages)
 {   
     [client
 fetchMessagesAfterMessage:messages.lastObject success:^(NSArray*nextMessages)
 {   
         NSLog(@"Fetched
 all messages.");   
     }
 failure:^(NSError*error)
 {   
         [selfpresentError:error];   
     }];   
}
 failure:^(NSError*error)
 {   
     [selfpresentError:error];   
}];}
 failure:^(NSError*error)
 {   
[selfpresentError:error];}]; //
      RAC实现:[[[[client
 logIn]   
then:^{   
     return[client
 loadCachedMessages];   
}]   
flattenMap:^(NSArray*messages)
 {   
     return[client
 fetchMessagesAfterMessage:messages.lastObject];   
}]   
subscribeError:^(NSError*error)
 {   
     [selfpresentError:error];   
}
 completed:^{   
     NSLog(@"Fetched
 all messages.");   
}]; | 
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 | __block
NSArray*databaseObjects;__block
NSArray*fileContents;NSOperationQueue*backgroundQueue
 = [[NSOperationQueuealloc]
 init];NSBlockOperation*databaseOperation
 = [NSBlockOperationblockOperationWithBlock:^{   
databaseObjects
 = [databaseClient fetchObjectsMatchingPredicate:predicate];}];NSBlockOperation*filesOperation
 = [NSBlockOperationblockOperationWithBlock:^{   
NSMutableArray*filesInProgress
 = [NSMutableArrayarray];   
for(NSString*path
 in files) {   
     [filesInProgress
 addObject:[NSDatadataWithContentsOfFile:path]];   
}   
fileContents
 = [filesInProgress copy];}];NSBlockOperation*finishOperation
 = [NSBlockOperationblockOperationWithBlock:^{   
[selffinishProcessingDatabaseObjects:databaseObjects
 fileContents:fileContents];   
NSLog(@"Done
 processing");}];[finishOperation
 addDependency:databaseOperation];[finishOperation
 addDependency:filesOperation];[backgroundQueue
 addOperation:databaseOperation];[backgroundQueue
 addOperation:filesOperation];[backgroundQueue
 addOperation:finishOperation];//
 RAC 实现RACSignal
 *databaseSignal = [[databaseClient   
fetchObjectsMatchingPredicate:predicate]   
subscribeOn:[RACScheduler
 scheduler]];RACSignal
 *fileSignal = [RACSignal startEagerlyWithScheduler:[RACScheduler scheduler] block:^(id<RACSubscriber>
 subscriber) {   
NSMutableArray*filesInProgress
 = [NSMutableArrayarray];   
for(NSString*path
 in files) {   
     [filesInProgress
 addObject:[NSDatadataWithContentsOfFile:path]];   
}   
[subscriber
 sendNext:[filesInProgress copy]];   
[subscriber
 sendCompleted];}];[[RACSignal   
combineLatest:@[
 databaseSignal, fileSignal ]   
reduce:^
id(NSArray*databaseObjects,
NSArray*fileContents)
 {   
     [selffinishProcessingDatabaseObjects:databaseObjects
 fileContents:fileContents];   
     returnnil;   
}]   
subscribeCompleted:^{   
     NSLog(@"Done
 processing");   
}]; | 
4, 简化集合操作
| 
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 | NSMutableArray*results
 = [NSMutableArrayarray];for(NSString*str
 in strings) {   
if(str.length
 < 2) {   
     continue;   
}   
NSString*newString
 = [str stringByAppendingString:@"foobar"];   
[results
 addObject:newString];} | 
//RAC实现:
| 
1 
2 
3 
4 
5 
6 
7 | RACSequence
 *results = [[strings.rac_sequence   
filter:^
BOOL(NSString*str)
 {   
     returnstr.length
 >= 2;   
}]   
map:^(NSString*str)
 {   
     return[str
 stringByAppendingString:@"foobar"];   
}]; | 
高大上函数响应式编程框架ReactiveCocoa学习笔记1 简介
标签:ios reactivecocoa 函数式 响应式 函数响应式
原文地址:http://blog.csdn.net/linjinxing/article/details/42106223