ReactiveCocoa介绍
ReactiveCocoa(简称为RAC
),是由Github开源的一个应用于iOS和OS开发的框架.是一个运用函数响应式编程(FRP)思想的框架.
ReactiveCocoa为事件提供了很多处理方法,利用 RAC 可以很方便将要处理的事件和监听的事件代码放在一起,这样可以方便管理代码.使用 RAC 可以不需要考虑调用顺序,而是直接考虑结果.
导入ReactiveCocoa
这里我们使用 cocoapod进行导入.在 podfile 中添加
pod 'ReactiveCocoa'
ReactiveCocoa基础类
在 RAC 中最常见的类是RACSignal
.在RAC 中最重要的概念就是信号.
1.RACSignal
RACSiganl
:只要有数据改变,就会把数据包装成一个信号传递出去.默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。
RACSubscriber
: 订阅者,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。
RACDisposable
:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。
什么是冷热信号?
冷热信号的概念源于.NET框架Reactive Extensions(RX)中的Hot Observable和Cold Observable,两者的区别是:
- Hot Observable是主动的,尽管你并没有订阅事件,但是它会时刻推送,就像鼠标移动;而Cold Observable是被动的,只有当你订阅的时候,它才会发布消息。
- Hot Observable可以有多个订阅者,是一对多,集合可以与订阅者共享信息;而Cold Observable只能一对一,当有不同的订阅者,消息是重新完整发送。
这里面的Observables可以理解为RACSignal。
RACSignal 的使用步骤:
- 创建信号: + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;
- 订阅信号: - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock;
- 发送信号: - (void)sendNext:(id)value;
// 创建信号
RACSignal *singal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 发送信号
[subscriber sendNext:@"发送信号"];
return [RACDisposable disposableWithBlock:^{
// 信号被取消订阅
// 清空资源
NSLog(@"取消订阅");
}];
}];
// 订阅信号
[singal subscribeNext:^(id x) {
NSLog(@"%@", x);
// 打印内容: "发送信号"
}];
2.RACSubject
RACSubject
:信号提供者,自己可以充当信号,又能发送信号。
RACSubject使用步骤
- 创建信号: + (instancetype)subject; 跟RACSiganl不一样,创建信号时没有block
- 订阅信号: - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock
- 发送信号: - sendNext:(id)value
// 创建信号
RACSubject *subject = [RACSubject subject];
// 订阅信号
[subject subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
// 发送信号
[subject sendNext:@"subject发送信号"];
3. RACReplaySubject
RACReplaySubject
:RACSubject的一个子类,用于保存发送过的值,当被订阅时,会向订阅者重新发送这些值。使用步骤和RACSubject类似.不同的是,RACReplaySubject可以先发送信号,再订阅信号.RACSubject不可以.
ReactiveCocoa 集合类
1.RACTuple
RACTuple
:元组.用于对数据的包装,( ps:对于使用过 swift 的人来说应该不陌生.)使用方法和数组类似
RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"123", @"321"]];
NSString *str = tuple[0];
// str 的值是"123"
2.RACSequence
RACSequence
:集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。
- 数组转化为集合类
NSArray *arr = @[@"123", @"341", @"213"];
// 将数组转化为集合
RACSequence *squence = arr.rac_sequence;
// 将集合转化为信号(遍历集合里的所有元素)
[squence.signal subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
- 字典转化为集合类
NSDictionary *dict = @{@"name":@"明月钓无痕", @"age":@"26"};
[dict.rac_sequence.signal subscribeNext:^(id x) {
// 遍历所有的元素
NSLog(@"%@", x); // x 是元组类型,每一个键值对,包装成一个元组.
// 用来解析元组的宏 RACTupleUnpack
// 宏里面的参数 传需要解析的变量名
// = 右边 放需要解析的元组
RACTupleUnpack(NSString *key, NSString *value) = x;
NSLog(@"%@--%@", key, value);
}];
ReactiveCocoa 事件处理类
RACCommand
:RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。
使用步骤
1.创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock
2.在signalBlock中,创建RACSignal,并且作为signalBlock的返回值
3.执行命令 - (RACSignal *)execute:(id)input
// 1. 创建命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
// input: 执行命令时传入参数
// 执行命令时调用 block
NSLog(@"%@", input);
// 2. 创建信号
// 不能返回 nil. 如果要返回空信号使用 empty
// return [RACSignal empty];
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"====");
[subscriber sendNext:@"请求数据"];
// 数据传递完,最好调用sendCompleted,这时命令才执行完毕。
[subscriber sendCompleted];
return nil;
}];
}];
// 3. 执行命令
RACSignal *signal = [command execute:@1];
[signal subscribeNext:^(id x) {
NSLog(@"x");
}];
使用场景:
- 在RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求。
- 当RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了。
ReactiveCocoa 中常见的宏
RAC(TARGET, ...): 给某个对象的某个属性绑定一个信号,只要信号内容改变,就会把内容赋值给属性
RACObserve(TARGET, KEYPATH):监听某个对象的某个属性,返回的是信号.
@weakify(Obj)和@strongify(Obj): 用于解决循环引用的问题
RACTuplePack(...): 用于把数据包装成元组
RACTupleUnpack(...):用于解析元组
ReactiveCocoa 常见用法
- 代替代理:- (RACSignal *)rac_signalForSelector:(SEL)selector;
- 代替通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UITextFieldTextDidChangeNotification object:nil] subscribeNext:^(NSNotification *x) {
}];
// 监听文本
[[self.userName rac_textSignal] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];
- 代替 KVO
DZRedView *view = [[DZRedView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
[self.view addSubview:view];
// 需要手动包含头文件 #import <NSObject+RACKVOWrapper.h>
// [view rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
// NSLog(@"%@, %@", value, change);
// }];
// view.x = 200;
[[view rac_valuesAndChangesForKeyPath:@"frame" options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld observer:nil] subscribeNext:^(id x) {
NSLog(@"====%@", x);
}];
view.x = 100;
- 监听事件点击
UIButton *btn = [[UIButton alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
btn.backgroundColor = [UIColor redColor];
[self.view addSubview:btn];
[[btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"%@", x);
}];