demo已经放到github
上.
一.Getting started
项目中使用ReactiveCocoa
在这里笔者使用Cocoapods
安装ReactiveCocoa
,在项目中创建podfile
文件,使用的是2.5
版本.
platform :ios, '8.0'
#use_frameworks!
target '你的项目名称’ do
pod 'ReactiveCocoa', '~> 2.5’
end
执行安装命令
pod install --no-repo-update
二.ReactiveCocoa常见类
1.RACSignal
信号类
RACSignal
信号类表示当数据改变时,在信号内部会利用订阅者发送数据.RACSignal
默认是一个冷信号
,只有被订阅以后才会变成热信号.
RACSignal
信号类的简单使用:
// 1.创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.利用订阅者发送数据
// 只有当有订阅者订阅时,才会调用这个block
[subscriber sendNext:@"这是发送的数据"];
return nil;
}];
// 2.订阅信号
[signal subscribeNext:^(id x) {
NSLog(@"接收到数据:%@",x);
}];
2.RACSubscriber
订阅者
RACSubscriber
是一个协议,任何遵循RACSubscriber
协议的对象并且实现协议方法都可以是一个订阅者,订阅者可以帮助信号发送数据.
RACSubscriber
协议中有四个方法.
- (void)sendNext:(id)value;
- (void)sendError:(NSError *)error;
- (void)sendCompleted;
- (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
3.RACDisposable
RACDisposable
用于取消订阅和清理资源,当信号发送完成或发送错误时会自动调用.
// 1.创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.利用订阅者发送数据
[subscriber sendNext:@"这是发送的数据"];
// 如果为未调用,当信号发送完成或发送错误时会自动调用
return [RACDisposable disposableWithBlock:^{
NSLog(@"资源被清理了");
}];
}];
// 2.订阅信号
[signal subscribeNext:^(id x) {
NSLog(@"接收到数据:%@",x);
}];
4.RACSubject
信号提供者
RACSubject
继承RACSignal
,又遵循了RACSubscriber
协议,所以既可以充当信号,又可以发送信号,通常用它代替代理.
// 1.创建信号
RACSubject *subject = [RACSubject subject];
// 2.订阅信号
[subject subscribeNext:^(id x) {
NSLog(@"接收到数据:%@",x);
}];
// 3.发送信号
[subject sendNext:@"发送数据"];
RACSubject
的底层实现
- 在执行
[RACSubject subject]
时,RACSubject
会在初始化时创建disposable
对象属性和subscribers
订阅者数组.
- 在执行
subscribeNext
订阅信号时,会创建一个订阅者RACSubscriber
,并将订阅者RACSubscriber
添加到subscribers
订阅者数组.
- 在执行
sendNext
发送信号时,会遍历subscribers
订阅者数组,执行sendNext
5.RACReplaySubject
RACReplaySubject
重复提供信号类,RACSubject
的子类.由于RACReplaySubject
的底层实现和RACSubject
不同,RACReplaySubject
可以先发送数据,再订阅信号.
// 1.创建信号
RACReplaySubject *replaySubject = [RACReplaySubject subject];
// 2.订阅信号
[replaySubject subscribeNext:^(id x) {
NSLog(@"订阅信号,%@",x);
}];
// 3.发送数据
[replaySubject sendNext:@"发送的数据"];
RACReplaySubject
的底层实现
- 在执行
[RACReplaySubject subject]
时,创建一个valuesReceived
数组
- 在执行
subscribeNext
时,创建订阅者,遍历valuesReceived数组,利用订阅者执行sendNext
发送valuesReceived
中的数据.
- 在执行
sendNext
时,将要发送的数据保存到valuesReceived数组中,执行sendNext
6.RACMulticastConnection
我们在使用RACsignal
,RACReplaySubject
或者RACReplaySubject
时,当一个信号被多个订阅者订阅时,在信号内部的block
或被调用多次,有时这样并不能满足我们的需求,我们想要信号被多个订阅者订阅时,信号内部的block
只被执行一次,那么RACMulticastConnection
就能帮助我们完成需求.
// 1.创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 3.发送数据
NSLog(@"发送数据");
[subscriber sendNext:@"发送数据"];
return nil;
}];
RACMulticastConnection *connection = [signal publish];
// 2.订阅信号
[connection.signal subscribeNext:^(id x) {
NSLog(@"接收到数据1:%@",x);
}];
[connection.signal subscribeNext:^(id x) {
NSLog(@"接收到数据2:%@",x);
}];
// 4.激活信号
[connection connect];
在log
中的打印如下
2017-06-20 16:55:50.809 MVVMRACDemoOC[2848:856666] 发送数据
2017-06-20 16:55:50.810 MVVMRACDemoOC[2848:856666] 接收到数据1:发送数据
2017-06-20 16:55:50.810 MVVMRACDemoOC[2848:856666] 接收到数据2:发送数据
7.RACCommand
RACCommand
是处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中.
使用一个demo
说明RACCommand
:监听按钮的点击,发送网络请求.
// 1.创建命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
NSLog(@"接收到命令:%@", input);
// 返回一个信号,不能为空.(信号中的信号)
// 3.创建信号用来传递数据
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@"信号中的信号发送的数据"];
// 注意:数据传递完成,要调用sendCompleted才能执行完毕
[subscriber sendCompleted];
return nil;
}];
}];
self.command = command;
// 2.订阅信号中的信号(必须要在执行命令前订阅)
[command.executionSignals.switchToLatest subscribeNext:^(id x) {
NSLog(@"接收到信号中的信号发送的数据:%@",x);
}];
// 4.执行命令
[command execute:@1];
[[command.executing skip:1] subscribeNext:^(id x) {
if ([x boolValue] == YES) {
NSLog(@"正在执行");
}else{
NSLog(@"未开始/执行完成");
}
}];
在log
中的打印
2017-06-20 17:26:28.013 MVVMRACDemoOC[3166:970238] 接收到命令:1
2017-06-20 17:26:28.016 MVVMRACDemoOC[3166:970238] 正在执行
2017-06-20 17:26:28.016 MVVMRACDemoOC[3166:970238] 接收到信号中的信号发送的数据:信号中的信号发送的数据
2017-06-20 17:26:28.017 MVVMRACDemoOC[3166:970238] 未开始/执行完成
三.常见宏
-
RAC(TARGET, [KEYPATH, [NIL_VALUE]])
给某个对象的某个属性做绑定.
// 只要passwordTextField内容变化,accountTextField的text就会跟着改变
RAC(self.accountTextField, text) = self.passwordTextField.rac_textSignal;
-
RACObserve(self, name)
监听某个对象的某个属性,返回信号
// 监听passwordTextField背景色的改变
[RACObserve(self.passwordTextField, backgroundColor) subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
-
@weakify(Obj)
和@strongify(Obj)
一般用来防止循环引用,组合使用. -
RACTuplePack
把数据包装成RACTuple(元组类)
// 把参数中的数据包装成元组
RACTuple *tuple = RACTuplePack(@1,@2);
-
RACTupleUnpack
把RACTuple(元组类)解包成对应的数据
// 把参数中的数据包装成元组
RACTuple *tuple = RACTuplePack(@"OneAlon",@"HangZhou");
// 解包元组,会把元组的值,按顺序给参数里面的变量赋值
RACTupleUnpack(NSString *name,NSNumber *address) = tuple;
四.常见用法
- 代替代理
代替代理有两种方法,一种是使用RACSubject
代替代理,另一种是使用rac_signalForSelector
方法代替代理.
这里模拟一个需求:自定义一个红色的view,在view中有一个按钮,监听按钮的点击.
如果不使用RAC
,在红色的view
中定义一个代理属性,点击按钮的时候通知代理做事情.
如果使用RAC
,直接让红色的view
调用rac_signalForSelector
方法即可.
[[self.redView rac_signalForSelector:@selector(buttonClick:)] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
2.代替KVO
监听红色view
的背景色的改变
[[self.redView rac_valuesAndChangesForKeyPath:@"backgroundColor" options:NSKeyValueObservingOptionNew observer:nil] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
3.监听事件
[[self.button rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(id x) {
NSLog(@"按钮被点击了");
}];
4.代替通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(id x) {
NSLog(@"键盘弹出");
}];
5.监听文本框文字改变
[_textField.rac_textSignal subscribeNext:^(id x) {
NSLog(@"文字改变了%@",x);
}];
6.处理当界面有多次请求时,需要都获取到数据时,才能展示界面,rac_liftSelector:withSignalsFromArray:Signals
五.参考文献
Objc中国 MVVM 介绍
ReactiveCocoa 和 MVVM 入门
最快让你上手ReactiveCocoa之基础篇
最快让你上手ReactiveCocoa之进阶篇
学习视频