ReactiveCocoa(RAC(是一个在KVO的基础上建立Objective-C的框架,函数式响应式框架,提供了组合和转化数据流的API.将iOS中的Action、Delegate、Target、KVO、Block,NSNotification统一共同的消息传递机制进行事件处理,属性变化,回调响应.
ReactiveCocoa为事件定义了一个标准接口,从而可以使用一些基本工具来更容易的连接、过滤和组合.RACSignal作为事件源,会给它的订阅者(subscribers)发送一连串的事件.Signal有三种事件:next,error和completed,可以在error或completed事件发出前发出任意多的next事件.
RAC为UIKit添加了很多类别来让我们可以订阅UI组件的事件,比如UITextField (RACSignalSupport)中的rac_textSignal会在文本域内容变化时发出next事件,事件包含的内容需要是对象类型,如果是数字类型,布尔值等字面量,可以用 @() 语法装箱成NSNumber对象.ReactiveCocoa对常用的UI控件进行了扩展,如图所示:
消息事件
RAC信号作为传递信号的工具,当数据变化时,信号就会发送改变的信息,以通知信号的订阅者执行方法.默认信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发.
KVO
定义课程Course类,定义如下:
`@interface Course : NSObject
@property (copy, nonatomic) NSString *courseName;
@property (assign, nonatomic) NSInteger courseType;
@end`
CourseName与CourseLabel绑定:
` @weakify(self)
[RACObserve(self.course, courseName)
subscribeNext:^(id x) {
@strongify(self)
NSLog(@"键值对变化--%@",x);
self.nameLabel.text = x;
}];`
按钮点击的时候改变课程名字:
- (IBAction)courseAction:(UIButton *)sender { self.course.courseName = [NSString stringWithFormat:@"FlyElephant--%d",arc4random_uniform(25)]; }
UITextField 事件监听
@weakify(self) [self.textField.rac_textSignal subscribeNext:^(id x) { @strongify(self) NSLog(@"TextField变化--%@",x); self.course.courseName = x; }];
信号绑定
信号绑定最常见的常见就是注册过程用户名和密码如果都有数据按钮可以点击,否则就不可以点击:
` id textFieldSignals = @[self.textField.rac_textSignal,self.passWordTextField.rac_textSignal];
@weakify(self);
[[RACSignal combineLatest:textFieldSignals] subscribeNext:^(RACTuple *x) {
@strongify(self);
NSString *name = [x first];
NSString *passWord = [x second];
if (name.length && passWord.length) {
self.loginButton.backgroundColor = [UIColor redColor];
self.loginButton.userInteractionEnabled = YES;
} else {
self.loginButton.backgroundColor = [UIColor darkGrayColor];
self.loginButton.userInteractionEnabled = NO;
}
}];
`
UIButton按钮事件
@weakify(self); [[self.loginButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) { @strongify(self); NSLog(@"用户名:--%@---密码:%@",self.textField.text,self.passWordTextField.text); }];
Delegate事件
RAC中通过RACDelegateProxy实现Delegate,但是有一点需要在类中声明Delegate变量,避免提前释放:
@weakify(self); self.textDelegate = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITextFieldDelegate)]; [[self.textDelegate rac_signalForSelector:@selector(textFieldShouldReturn:)] subscribeNext:^(id x) { @strongify(self); NSLog(@"FlyElephant---代理执行"); [self.passWordTextField becomeFirstResponder]; }]; self.textField.delegate = (id)self.textDelegate;
NSNotification通知事件
NSNotification事件执行之后可以传递给next进行事件处理:
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) { NSLog(@"键盘弹出事件"); }];
信号处理
RACSignal中很多方法用于订阅事件,每个方法都会将类型为(void (^)(id x))的block作为参数,当事件发生时block中的代码会执行,例如 subscribeNext: 方法会传入一个block作为参数,当Signal的next事件发出后,block会接收到事件并执行.
RACSignal还有一些方法是对Signal做操作的, 在RACSignal (Operations)类别还有其他的方式,map,filter,throttle....
创建信号
` RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"FlyElephant"];
[subscriber sendCompleted];
return nil;
}];
//订阅信号
[signal subscribeNext:^(id x) {
NSLog(@"信号值 = %@", x);
} error:^(NSError *error) {
NSLog(@"error = %@", error);
} completed:^{
NSLog(@"completed");
}];`
Map
[[self.textField.rac_textSignal map:^id(NSString *value) { return @(value.length); }] subscribeNext:^(id x) { NSLog(@"信号量Map:%@",x); }];
Filter
[[self.textField.rac_textSignal filter:^BOOL(NSString *value) { return [value length] > 5; }] subscribeNext:^(id x) { NSLog(@"value字符串变换 = %@", x); }];
Take/Skip
` RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"1"];
[subscriber sendNext:@"2"];
[subscriber sendNext:@"3"];
[subscriber sendNext:@"4"];
[subscriber sendNext:@"5"];
[subscriber sendCompleted];
return nil;
}];
RACSignal *takeSignal = [signal take:2];
[takeSignal subscribeNext:^(id x) {
NSLog(@"Take---%@", x);
} completed:^{
NSLog(@"Take---FlyElephant---completed");
}];
RACSignal *skipSignal = [signal skip:2];
[skipSignal subscribeNext:^(id x) {
NSLog(@"Skip---%@", x);
} completed:^{
NSLog(@"Skip---completed");
}];`
Delay
` RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
[subscriber sendNext:@"FlyElephant"];
[subscriber sendCompleted];
return nil;
}] delay:2];
NSLog(@"FlyElephant-delay");
[signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];`
throttle
在做搜索框的时候,有时需要实时搜索,即用户每每输入字符,view都要求展现搜索结果.这时如果用户搜索的字符串较长,那么由于网络请求的延时可能造成UI显示错误,并且多次不必要的请求还会加大服务器的压力,这显然是不合理的,此时我们就需要用到节流.
[[self.textField.rac_textSignal throttle:0.5] subscribeNext:^(id x) { NSLog(@"Throttle---%@", x); }];
distinctUntilChanged
网络请求中为了减轻服务器压力,无用的请求我们应该尽可能不发送,distinctUntilChanged的作用是使RAC不会连续发送两次相同的信号.
[[self.textField.rac_textSignal distinctUntilChanged] subscribeNext:^(id x) { NSLog(@"FlyElephant---防止重复请求:%@",x); }];
timeOut
` RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id subscriber) {
[[RACScheduler mainThreadScheduler] afterDelay:3 schedule:^{
[subscriber sendNext:@"FlyElephant--TimeOut"];
[subscriber sendCompleted];
}];
return nil;
}] timeout:2 onScheduler:[RACScheduler mainThreadScheduler]];
[signal subscribeNext:^(id x) {
NSLog(@"TimeOut---%@", x);
} error:^(NSError *error) {
NSLog(@"TimeOut---%@", error);
}];`
ignore
[[self.textField.rac_textSignal ignore:@"FlyElephant"] subscribeNext:^(id x) { NSLog(@"ignore---%@", x); }];
ReactiveCocoa实际使用过程中还有无数种演绎用法,本文只是介绍其中的九牛一毛,如果有写的不恰当的地方,欢迎指正~