
今天主要分享内容:
- ReactiveCocoa简单介绍
- 响应式编程和函数编程的概述
- RACSignal 信号
- RACSignald订阅过程
- First Demo
- Second Demo
- 参考链接
ReactiveCocoa
- GitHub mac 客户端副产物
- FRP(响应式函数编程)在cocoa框架的实现
- ReactiveCocoa可以对代理模式, target-action模式,键值对观察(KVO)等进行替代
响应式函数编程概述
ReactiveCocoa框架让你可以在iOS应用中使用响应式函数编程(FRP)
- 命令式编程 VS 函数式编程 VS 响应式编程(粗浅介绍)
int func(int x)
{
int result = 1;
for (int i = 1; i <=x; ++i)
{
result *= i;
}
} ```
命令式编程关心解决问题的步骤
你得把达到目的的步骤详细的描述出来,
然后交给机器去运行。
需要管理非常多的中间状态量,代码繁杂。
int func(int x)
{
if (x == 1) return 1;
return x * func(x - 1);
} ```
函数式编程关心数据的映射
这里的映射就是数学上「函数」的概念,
一种东西和另一种东西之间的对应关系。
没有状态量,简洁高效。
void test() {
int a = 5;
int b = 6;
int c = a + b;
int d = funAdd(&a , &b);//a b中的值
a = 10;
NSLog(@"%d",c);
} ```
响应式编程是一种面向数据流和变化传播的编程范式。
在响应式编程中d的值会随着a或b的更新而更新。
### RACSignal 信号
ReactiveCocoa四大核心组件构成:
信号源:RACStream 及其子类;
订阅者:RACSubscriber 的实现类及其子类;
调度器:RACScheduler 及其子类;
清洁工:RACDisposable 及其子类。
其中,信号源又是最核心的部分,其他组件都是围绕它运作的。
ReactiveCocoa使用信号来代表异步事件,提供了一种统一的方式来处理所有异步的行为,包括代理方法、block 回调、target-action 机制、通知、KVO 等:
// 代理方法
[[self
rac_signalForSelector:@selector(webViewDidStartLoad:)
fromProtocol:@protocol(UIWebViewDelegate)]
subscribeNext:^(id x) {
// 实现 webViewDidStartLoad: 代理方法
}];
// target-action
[[self.avatarButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(UIButton *avatarButton) {
// avatarButton 被点击了
}];
// 通知
[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:kReachabilityChangedNotification object:nil]
subscribeNext:^(NSNotification *notification) {
// 收到 kReachabilityChangedNotification 通知
}];
// KVO
[RACObserve(self, username) subscribeNext:^(NSString *username) {
// 用户名发生了变化
}];
### RACSignald订阅过程
// 创建信号 并发送信号
//使用RACSignal的createSignal:方法创建信号。描述这个信号的block是这个方法唯一的入参。当这个信号有订阅者的时候,block中的代码就会执行。
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"dispose");
}];
}];
// 订阅信号
RACDisposable *disposable = [signal subscribeNext:^(id x) {
NSLog(@"next value is %@",x);
} error:^(NSError *error) {
NSLog(@"error -> %@",[error description]);
} completed:^{
NSLog(@"It finished success");
}];
[disposable dispose];
//ReactiveCocoa的信号(表现为RACSignal类)会向他的订阅者们发送事件流。
//发送的事件分为三种类型:next,error 和 completed。
//一个信号在因为报错或完成的终止前可以发送若干个事件。
### First Demo
实现一个登陆界面

要求:
1. User & Password不少于3位
2. 低于3位显示红色,正常显示蓝色
3. 登录按钮只有在用户名和密码都有效输入时才能使用
Answer 1
//ReactiveCocoa框架使用category为很多标准的UIKit控件添加了信号
//可以对它们的各种事件进行订阅
//1.创建信号
RACSignal *userName = [self.usernameTextField rac_textSignal];
//2.筛选
RACSignal *validUsername = [userName filter:^BOOL(id value) {
NSString *text = value;
return [self isValidUsername:text];
}];
//3.调用信号RACSignal的subscribeNext就能订阅,
//subscribeNext:方法就提供了一个block,
//在每个next事件到达时执行里面的代码。
[validUsername subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
Answer 2
[[self.usernameTextField.rac_textSignal
map:^id(NSString *username) {
return [self isValidUsername:username] ? [UIColor whiteColor] : [UIColor redColor];
}]
subscribeNext:^(UIColor *color) {
self.usernameTextField.backgroundColor = color;
}];
[[self.passworkTextField.rac_textSignal
map:^id(NSString *password) {
return [self isValidUsername:password] ? [UIColor whiteColor] : [UIColor redColor];
}]
subscribeNext:^(UIColor *color) {
self.passworkTextField.backgroundColor = color;
}];```
Answer 3
//创建两个发送布尔值的信号validUsernameSignal、validPasswordSignal
//对用户名和密码的输入进行验证。
RACSignal *validUsernameSignal =
[self.usernameTextField.rac_textSignal
map:^id(NSString *text) {
return @([self isValidUsername:text]);
}];
RACSignal *validPasswordSignal =
[self.passworkTextField.rac_textSignal
map:^id(NSString *text) {
return @([self isValidPassword:text]);
}];
//信号合成
//接下来的任务就是合成这两个信号,用以共同决定登录按钮是否可用
RACSignal *signUpActiveSignal =
[RACSignal combineLatest:@[validUsernameSignal, validPasswordSignal]
reduce:^id(NSNumber *usernameValid, NSNumber *passwordValid) {
return @([usernameValid boolValue] && [passwordValid boolValue]);
}];
//上面的代码使用combineLatest:reduce:方法 (RACSignal合成方法可以合成任意数量的信号)
//获取validUsernameSignal和validPasswordSignal的最近一个信号值并组合成一个全新的信号。
//每当两个源信号的其中一个发送新值,
//reduce里的block代码块就会执行(reduce中 block的入参和源信号一一对应)
//其返回的值会作为合成信号的值发送出去。
//最后是订阅
[signUpActiveSignal subscribeNext:^(NSNumber *signupActive) {
self.signinBnt.enabled = [signupActive boolValue];
}];
Second Demo

1.主动获取数据并刷新TableView
2.根据搜索关键字获取指定数据,并刷新TableView;
Demo地址
参考链接
Reactive github
美团的几篇博客
RACSignal的Subscription深入分析
raywenderlich 上的文章:
http://www.raywenderlich.com/62699/reactivecocoa-tutorial-pt1、http://www.raywenderlich.com/62796/reactivecocoa-tutorial-pt2、http://www.raywenderlich.com/74106/mvvm-tutorial-with-reactivecocoa-part-1、http://www.raywenderlich.com/74131/mvvm-tutorial-with-reactivecocoa-part-2。
网友评论