RedRain的简书://www.greatytc.com/users/29e03e6ff407/latest_articles
1,ReactiveCocoa
RAC是GitHub开源的框架
2,ReactiveCocoa作用
框架特殊处理了: action, delegate, KVO, callback
如:可以把按钮的target-action聚合在一起, 把UI逻辑处理代码聚合在一起.
3,编程思想
在开发中我们也不能太依赖于某个框架, 否则这个框架不更新了, 导致项目后期无法维护.因此学习一个框架, 还是有必要了解它的编程思想
.
3.1面向过程
处理事情以过程为核心, 一步步实现
3.2面向对象
万物皆对象
3.3链式编程思想
将多个操作通过
.
连接在一起成为一句代码, 使得代码可读性提高.
a(1).b(2).c(3)
特点:
方法的返回值是block, block必须有返回值(对象本身), block参数(需要处理的值)
其中代表的框架 masrony
3.4响应式编程思想
不需要考虑调用顺序, 只需要知道结果, 类似于蝴蝶效应, 产生一个事件,会影响很大东西, 这些事件像流一样的传播出去, 然后影响结果, 借用面向对象的一句话, 万物皆是流.
- 代表: KVO运用
3.5函数式编程思想
是把操作尽量写成一系列嵌套的函数或者方法调用.
本质:
就是往方法中传入block, 方法中嵌套block调用, 把代码聚合起来管理.
特点:
每个方法有返回值(对象本身), 把函数或者Block当作参数, block参数(需要操作的值) block返回值(操作结果).
代表:
ReactiveCocoa
4.ReactiveCocoa
4.1 RACSignal
有数据产生就会,就使用RACSignal.
- 1,创建信号(冷信号)
- 2,订阅信号(热信号)
- 3,发送信号
4.2 RACDisposable
用于取消订阅或者清理资源, 当信号发送完成或者发送错误时候, 就会自动触发它.
- 使用场景: 不想监听某个信号时, 可以通过它主动取消订阅信号.
在生成信号的返回值中, 返回RACDisposable对象, 如果创建了这个Block对象, 会在订阅信号后, 调用这个block, 如果在外界强引用这个Disposable对象, 那么不会自动的调用这个Disposable的block, 但是可以通过 signal的 subscribeNext方法的返回对象,disposable主动调用 dispose方法来触发这个block.
4.3 RACSubject
信号提供者, 自己可以充当信号, 又能发送信号.
- 使用场景: 通常来代替代理, 有了它, 就不必要定义代理了.
RACSubject 发送信号的方式和Signal是不同的, 他的操作步骤中 subscribeNext 只是保存了Next
block代码. 在subject的SendNext中,会遍历subject中所有的订阅者, 向每一个订阅者发送消息.
4.4 RACReplaySubject
重复提供信号类, RACSubject的子类.
这个类比较特殊,复习时仔细阅读以下1,2,3的解释来理解与其他类的区别
- 1,创建信号
- 2,订阅信号: 遍历所有sendNext时保存的值, 向 当前 订阅者去发送数据.
- 3,发送信号: 先保存这个值, 遍历 所有 订阅者,发送数据.
所有这个类可以 先发送信号, 再订阅信号
4.5 RACTuple
元祖类, 类似NSArray, 用来包装值.
4.6 RACSequence
RAC中的集合类, 用于代替NSArray, NSDictionary, 可以使用它们来快速遍历数组和字典.
- 使用场景: 字典转模型.
NSArray转换为Sequence
NSArray *arr = @[@"123",@"123",@"123",@"123"];
[arr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
这个方法会直接遍历数组中的每一个值进行订阅. 不需要sendNext方法.
NSDictionary 转换为 Sequence
NSDictionary *dic = @{@"name":@"123",@"age":@"123"};
[dic.rac_sequence.signal subscribeNext:^(RACTuple *x) {
NSString *key0 = x[0];
NSString *value0 = x[1];
NSLog(@"key0:%@ ,value0:%@",key0,value0);
RACTupleUnpack(NSString *key1, NSString *value1) = x;
NSLog(@"key1:%@ ,value1:%@",key1,value1);
}];
打印的信息:
key0:name ,value0:123
key1:name ,value1:123
key0:age ,value0:123
key1:age ,value1:123
应用字典转模型
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"source.plist" ofType:nil];
NSArray *dicArr = [NSArray arrayWithContentsOfFile:filePath];
NSMutableArray *mArr = [NSMutableArray arrayWithCapacity:3];
[dicArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
SourceModel *model = [[SourceModel alloc]init];
model.name = x[@"name"];
model.age = x[@"age"];
[mArr addObject:model];
}];
高级用法: Map
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"source.plist" ofType:nil];
NSArray *dicArr = [NSArray arrayWithContentsOfFile:filePath];
NSArray *modelArray = [[dicArr.rac_sequence map:^id(NSDictionary *value) {
SourceModel *model = [[SourceModel alloc]init];
model.name = x[@"name"];
model.age = x[@"age"];
return model;
}] array];
5.ReactiveCocoa开发中常见用法
5.1 代替代理
RACSubject
rac_signalForSelector: 监听某个方法被调用了转化为信号, 就可以订阅了
[[self rac_signalForSelector:@selector(didReceiveMemoryWarning)]
subscribeNext:^(id x) {
}];
5.2 代替KVO
1. rac_observeKeyPath
#import "NSObject+RACKVOWrapper.h"
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self.view rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
}];
}
2. rac_valuesForKeyPath
此方法把监听变化转化为一个信号, 成为信号后,即可订阅
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[[self.view rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id x) {
}];
}
5.3 监听事件
监听按钮点击事件
UIButton *btn = [[UIButton alloc]init];
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(id x) {
}];
5.4 代替通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil]
subscribeNext:^(id x) {
}];
5.5 监听文本框
把文本框内容的变化,转化为一个信号,进行订阅.
UITextField *field = [UITextField new];
[field.rac_textSignal subscribeNext:^(id x) {
}];
5.6 处理界面有多次请求时, 需要都获取到数据时才能展示界面
- rac_liftSelector:withSignalsFromArray:
这个方法中,第二个参数传入信号数组, 对应的第一个参数的回调方法就需要几个参数与之对应.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
RACSignal *sin1 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"发送一个数据"];
return nil;
}];
RACSignal *sin2 = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"又发送一个数据"];
return nil;
}];
// 此方法会等到其数组内的信号全部sendNext后,才会执行selector.
[self rac_liftSelector:@selector(updateUIWithData1:data2:) withSignalsFromArray:@[sin1,sin2]];
}
- (void)updateUIWithData1:(NSString *)stringFirst data2:(NSString *)stringSecound{
/**
* 参数的数据是和方法数组中信号的顺序一致.
*/
NSLog(@"sin1Send:%@ sin2Send:%@",stringFirst,stringSecound);
}
6.ReactiveCocoa常见的宏
6.1 RAC(<#TARGET, ...#>)
用来给某个对象的某个属性绑定信号, 只要产生信号内容, 就会把内容给属性赋值.
UILabel *label = [UILabel new];
UITextField *textField = [UITextField new];
// 普通写法
[textField.rac_textSignal subscribeNext:^(id x) {
label.text = x;
}];
// RAC宏写法
// 用来给某个对象的某个属性绑定信号, 只要产生信号内容, 就会把内容给属性赋值.
RAC(label,text) = textField.rac_textSignal;
6.2 RACObserve(<#TARGET#>, <#KEYPATH#>)
// 1, 普通监听属性的方法
[self.view rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
NSLog(@"%@",value);
}];
// 2,RACObserve
[RACObserve(self.view, frame) subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
6.3 RACTuplePack 和 RACTupleUnpack
RACTuple *tuple = RACTuplePack(@1,@2);
NSLog(@"%@", tuple[0]);
RACTupleUnpack(NSNumber *number1, NSNumber *number2) = tuple;
NSLog(@"%@ %@", number1, number2);
7. RACMulticastConnection
用于当一个信号, 被多次订阅时, 为了保证创建信号时, 避免多次调用创建信号中的block, 造成副作用, 可以使用这个类处理.
RACMulticastConnection
这个类是结合了 RACSignal
与 RACSubject
两个类来使用.
每个订阅者都保存在 RACMulticastConnection
的RACSubject
中.
RACSubject
作为 RACSignal
的 RACSubscriber
用来 sendNext
.
RACSubject sendNext
会遍历其中的所有的 订阅者 来 sendNext
- 可以查看
RACMulticastConnection
的connect
方法
// 1,创建信号
RACSignal *sin = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"请求数据");
[subscriber sendNext:@"发送数据"];
return nil;
}];
// 2,制作中间连接层
RACMulticastConnection *connection = [sin publish];
// 3,通过连接层订阅消息.
[connection.signal subscribeNext:^(id x) {
NSLog(@"订阅者1");
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"订阅者2");
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"订阅者3");
}];
// 4,开始连接
[connection connect];
8. RACCommand
RAC中用于处理事件的类, 可以把事件如何处理, 事件中的数据如何传递, 包装到这个类中, 它可以很方便的监控事件的执行过程.
- 使用场景:
监听按钮点击, 网络请求.
RACCommand *command = [[RACCommand alloc]initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"%@",input);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
// 发送完成后, 可以设置sendCompleted.
// 就可以改变command.executing中的bool值.
[subscriber sendCompleted];
return nil;
}];
}];
[command.executionSignals subscribeNext:^(RACSignal *x) {
[x subscribeNext:^(id x) {
NSLog(@"xxxx111:%@",x);
}];
}];
[command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"xxxx222:%@",x);
}];
[command.executing subscribeNext:^(id x) {
if ([x boolValue] == YES) {
// 在发送先后后
NSLog(@"正在执行.");
}else{
NSLog(@"执行完成/没有执行");
}
}];
[command execute:@100];
对应的打印信息
执行完成/没有执行
100
正在执行.
xxxx111:1
xxxx222:1
执行完成/没有执行