RAC浅浅析
ReactiveCocoa 可以说是结合了函数式编程和响应式编程的框架,也可称其为函数响应式编程(FRP)框架,强调一点,RAC虽然最大的优点是提供了一个单一的、统一的方法去处理异步的行为,包括delegate方法,blocks回调,target-action机制,notifications和KVO.
它最大的与众不同是提供了一种新的写代码的思维,由于RAC将Cocoa中KVO、UIKit event、delegate、selector等都增加了RAC支持,所以都不用去做很多跨函数的事。
如果全工程都使用RAC来实现,对于同一个业务逻辑终于可以在同一块代码里完成了,将UI事件,逻辑处理,文件或数据库操作,异步网络请求,UI结果显示,这一大套统统用函数式编程的思路嵌套起来,进入页面时搭建好这所有的关系,用户点击后妥妥的等着这一套联系一个个的按期望的逻辑和次序触发,最后显示给用户。
一、响应式编程就是实时响应某个事件
// 创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"Hello");
// 发送信号
[subscriber sendNext:@"This is RAC"];
return nil;
}];
// 订阅信号
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
二、RAC 的核心思想:创建信号 - 订阅信号 - 发送信号
举几个实际使用案例
1、RACSignal 信号
/* 创建信号 */
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
/* 发送信号 */
[subscriber sendNext:@"发送信号"];
return nil;
}];
/* 订阅信号 */
RACDisposable *disposable = [signal subscribeNext:^(id _Nullable x) {
NSLog(@"信号内容:%@", x);
}];
/* 取消订阅 */
[disposable dispose];
2、RACSubject 信号
和代理的用法类似,通常用来代替代理,有了它,就不必要定义代理了。
/* 创建信号 */
RACSubject *subject = [RACSubject subject];
/* 发送信号 */
[subject sendNext:@"发送信号"];
/* 订阅信号(通常在别的视图控制器中订阅,与代理的用法类似) */
[subject subscribeNext:^(id _Nullable x) {
NSLog(@"信号内容:%@", x);
}];
2、TargetAction转Block
①UITextField(实时监听输入框内容)
[[self.textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
NSLog(@"%@", x);
}];
②UITextField(实时监听多个输入框内容)
[[RACSignal combineLatest:@[self.nameTextField.rac_textSignal, self.pwdTextField.rac_textSignal]] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@"name : %@, pwd : %@", x.first, x.second);
}];
下面表示只有 用户名 和 密码 输入框内容都大于 0 时,登录 按钮才可以点击,而且状态是实时监听的,一句代码就能完成这个功能。
RAC(_loginButton, enabled) = [RACSignal combineLatest:@[_username.rac_textSignal, _password.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
return @(username.length && password.length);
}];
下面表示 用户名 和 密码 输入框内容事件
[[[RACSignal combineLatest:@[self.nameTextField.rac_textSignal, self.pwdTextField.rac_textSignal]] reduceEach:^id _Nullable(NSString *name, NSString *pwd) {
return @(name.length && pwd.length);
}] subscribeNext:^(id _Nullable x) {
NSLog(@"%zd", [x boolValue]);
}];
③UIButton(Button点击事件)
[[self.button rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"%@",x);
}];
3、通知转Block
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidBecomeActiveNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"%@",x);
}];
4、KVO转Block
[RACObserve(self.person, name) subscribeNext:^(id _Nullable x) {
self.label.text = x;
}];
5、 代替 Delegate 代理方法
可以省去监听以及设置 delegate 的步骤,下面表示只要 view 中执行了 btnClick 这个方法,就会发送信号执行这个回调。
[[view rac_signalForSelector:@selector(btnClick)] subscribeNext:^(RACTuple * _Nullable x) {
NSLog(@" view 中的按钮被点击了");
}];
6、代替 NSTimer 计时器
可以代替 NSTimer 使用。
@interface ViewController ()
@property (nonatomic, strong) RACDisposable *disposable;
@end
/* 定义计时器监听 */
self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
NSLog(@"当前时间:%@", x); // x 是当前的系统时间
/* 关闭计时器 */
[_disposable dispose];
}];
7、便利 Array 数组和 Dictionary 字典
可以省去使用 for 循环来遍历。
/* 遍历数组 */
NSArray *array = @[@"1", @"2", @"3", @"4", @"5"];
[array.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"数组内容:%@", x); // x 可以是任何对象
}];
/* 遍历字典 */
NSDictionary *dictionary = @{@"key1":@"value1", @"key2":@"value2", @"key3":@"value3"};
[dictionary.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
RACTupleUnpack(NSString *key, NSString *value) = x; // x 是一个元祖,这个宏能够将 key 和 value 拆开
NSLog(@"字典内容:%@:%@", key, value);
}];
三、注意事项:
RAC使用@weakify(self);和@strongify(self);来避免block循环引用
@weakify(self);
[[self.textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
@strongify(self);
self.textField.text = @"Hello";
}];
网友评论