起因:ReactiveCocoa一直没能找时间出来学习,也没有实际使用过,今天来图书馆一游,专门静下心解决这个课题.
一、环境配置
新建一个工程,这里遇到一个问题,ReactiveCocoa只能由Pod导入,又捣腾了CocoaPod的安装,好久没搞这个又花了点时间,把过程记录下来.
新工程CocoaPods的配置(只限已经安装了CocoaPods基础上)
1.命令行cd到工程目录
2.vim Podfile,创建Podfile,并编辑
target 'TestRAC' do
pod 'ReactiveCocoa', '~> 4.1.0'
end
3.命令行输入:pod update 更新本地pod库(这个过程比较漫长,我这边下载到本地的库有600M+,时间大概10分钟,可以查看/Users/mac/.cocoapods/ 文件夹大小确定是否在下载中..),最终命令行显示更新完库后.我们进入下一步.
4.命令行输入:pod update 创建工程的pod库和xcworkspace
等后片刻工程里已经生成xcworkspace,和已经安装好了ReactiveCocoa
运行后发现工程报错,报错信息如下:
The “Swift Language Version” (SWIFT_VERSION) build setting must be set to a supported value for targets which use Swift. Supported values are: 3.0, 4.0, 4.2. This setting can be set in the build settings editor.
问题原因:xcode10已经抛弃了swift2.0.编译项已经无法选择2.0.
解决:用xcode9打开工程,运行成功!
二、开始学习ReactiveCocoa
简单入门:
1、监听UITextField
//监听UITextField的输入,
//x为输入的全部文字
[[self.textF rac_textSignal]subscribeNext:^(NSString * _Nullable x) {
NSLog(@"%@",x);
}];
2、监听按钮点击
//监听按钮点击事件
//x为UIButton
RACDisposable *disposable = [[self.button rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"%@",x);
}];
3、把通知封装成block
//监听通知
//x为NSNotification,object为UIApplication
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidBecomeActiveNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"%@",x);
}];
4、自定义信号
//自定义信号
//信号新的订阅者会执行block里的所有方法
RACSignal *customSingal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"信号创建中");
[subscriber sendNext:@"lalala"];
NSLog(@"信号发送完毕");
return nil;
}];
[customSingal subscribeNext:^(id _Nullable x) {
NSLog(@"订阅者1 收到信号%@",x);
}];
[customSingal subscribeNext:^(id _Nullable x) {
NSLog(@"订阅者2 收到信号%@",x);
}];
信号的订阅者会执行block里的所有方法,那么我们只想执行信号发送这部分怎么办呢?
这时候有一个新类叫链接类:RACMulticastConnection,使用方法如下:
//自定义信号
//信号新的订阅者会执行block里的所有方法
RACSignal *customSingal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSLog(@"信号创建中");
[subscriber sendNext:@"lalala"];
NSLog(@"信号发送完毕");
return nil;
}];
//把信号转为链接类,链接类只执行信号创建时block中RACSubject产生的信号
RACMulticastConnection *connection = [customSingal publish];
[connection.signal subscribeNext:^(id _Nullable x) {
NSLog(@"链接器1 接收到信号");
NSLog(@"%@",x);
}];
[connection.signal subscribeNext:^(id _Nullable x) {
NSLog(@"链接器2 接收到信号");
NSLog(@"%@",x);
}];
//链接信号
[connection connect];
5.取消订阅信号
这里我们以取消按钮点击的例子为例:
//监听按钮点击事件
//x为UIButton
RACDisposable *disposable = [[self.button rac_signalForControlEvents:(UIControlEventTouchUpInside)] subscribeNext:^(__kindof UIControl * _Nullable x) {
NSLog(@"%@",x);
}];
//当想取消订阅的时候调用创建信号调用 dispose 就可以取消
//3秒后取消订阅
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//
[disposable dispose];
});
注意:block中的循环引用问题
RAC使用@weakify(self);和@strongify(self);来避免block循环引用
@weakify(self);
[[self.textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
@strongify(self);
self.textField.text = @"Hello";
}];
高级使用:
1、RACCommand常用的宏
1.1 RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
//RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某个属性绑定。
-(void)testMacroRAC{
RAC(self.label,text) = self.textF.rac_textSignal;
RAC(self.label2,text) = self.textF.rac_textSignal;
[self.textF.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
NSLog(@"%@",x);
}];
}
效果如下:
1.2 //RACObserve(self, name):监听某个对象的某个属性,返回的是信号。
//RACObserve(self, name):监听某个对象的某个属性,返回的是信号。
-(void)testMacroRACObserve{
RAC(self.label,text) = self.textF.rac_textSignal;
[RACObserve(self.label, text)subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
RAC(self.label2,text) = RACObserve(self.label, text);
}
RACCommand 这个用的不多,暂时不试验了
补充常用用法
代替代理:
rac_signalForSelector:用于替代代理。
RACSubject:也用于代理.
// 需求:自定义BlueView,监听BlueView中按钮点击
// 之前都是需要通过代理监听,点击按钮的时候,通知代理做事情
// rac_signalForSelector:把调用某个对象的方法的信息转换成信号,就要调用这个方法,就会发送信号。
// 这里表示只要blueView调用clickBlueViewButton,就会发出信号,订阅就好了。
[[self.blueView rac_signalForSelector:@selector(clickBlueViewButton)] subscribeNext:^(id x) {
NSLog(@"点击了蓝色视图上的按扭");
}];
代替KVO :
rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变。
[[self.blueView rac_valuesAndChangesForKeyPath:@"frame" options:NSKeyValueObservingOptionNewobserver:nil] subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
监听事件:
rac_signalForControlEvents:用于监听某个事件。
// 把按钮点击事件转换为信号,点击按钮,就会发送信号
//监听事件
[[self.bt rac_signalForControlEvents:UIControlEventTouchUpInside ] subscribeNext:^(id x) {
NSLog(@"点击了按扭");
}];
代替通知:
rac_addObserverForName:用于监听某个通知。
// 把监听到的通知转换信号
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillChangeFrameNotificationobject:nil] subscribeNext:^(id x) {
NSLog(@"键盘frame发生改变%@",x);
}];
监听文本框文字改变:
rac_textSignal:只要文本框发出改变就会发出这个信号。
// 5.监听文本框的文字改变
[self.textfield.rac_textSignal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];
关于信号接收者要dealloc的时候取消订阅
错误的方式如下:
- (void)viewDidLoad {
[super viewDidLoad];
//任务继续执行
//SecondVC dealloc
[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * _Nullable x) {
[SecondVC print];
}];
}
+(void)print{
NSLog(@"一秒执行一次");
}
-(void)dealloc{
NSLog(@"%@ dealloc",NSStringFromClass(self.class));
}
运行如下:
这种订阅方式在订阅者dealloc的时候并不会停止,订阅者的任务依然在执行
正确的方式如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor yellowColor];
// takeUntil 一直订阅直到收到后面的信号才停止订阅
[[[RACSignal interval:1 onScheduler:[RACScheduler mainThreadScheduler]]takeUntil:self.rac_willDeallocSignal]subscribeNext:^(NSDate * _Nullable x) {
[SecondVC print];
}];
}
+(void)print{
NSLog(@"一秒执行一次");
}
-(void)dealloc{
NSLog(@"%@ dealloc",NSStringFromClass(self.class));
}
运行如下:
我们看到在订阅者dealloc后,任务也停止了
takeUntil 一直订阅直到收到后面的信号才停止订阅,一般这样用,不然任务会一直执行
以上部分转载.
ReactiveCocoa学习参考:
简单入门:http://www.cocoachina.com/ios/20171227/21667.html
深入研究:
https://www.cnblogs.com/fengmin/p/5662270.html
https://www.jianshu.com/p/3db561083fef
https://blog.csdn.net/z929118967/article/details/75547326
(组合,聚合,同步等高级用法):
https://www.cnblogs.com/czc-wjm/p/5655282.html