本篇内容主要记录一下平时项目中经常会用到的rac的情况,不做太深入的研究。
前言
ReactiveCocoa 可以说是结合了函数式编程和响应式编程的框架,也可称其为函数响应式编程(FRP)框架,强调一点,RAC最大的优点是提供了一个单一的、统一的方法去处理异步的行为,包括delegate方法,blocks回调,target-action机制,notifications和KVO.
一.导入
1.在项目的podfile文件中添加
# RAC
pod 'ReactiveObjC'
2.执行pod install方法,即可导入框架。
3.在使用到rac的类中导入
//响应式编程
#import <ReactiveObjC/ReactiveObjC.h>
我的项目中因为很多类都会用到,所以直接在pch文件导入
二.rac常规使用
1).button添加点击事件
比如给一个登陆按钮添加点击事件
[[self.loginAccountBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
@strongify(self);
[self.navigationController pushViewController:[[NSClassFromString(@"GHLoginViewController") alloc] init] animated:YES];
}];
2).替代kvo监听
监听一些属性的变化,只要属性改变就会调用,并把改变的值传递给你。
//如:
@property(noatomic,assign) int age;
监听age的每一次赋值,并产生回调
[RACObserve(self, age) ]subscribeNext:^(id _Nullable x) {
NSLog(@"%@",x);
}];
监听age 的赋值,并忽略值为 “1” 的情况,不执行回调
[[RACObserve(self, networkStatus) ignore:@"1"]subscribeNext:^(id _Nullable x) {
}];
//模拟一个事件 触摸屏幕 就让age自增
-(void)touchesBegin:(NSSet<UITouch*>*)touches WithEvent:(UIEvent*)event{
age++;
}
//在任意类中监听networkStatus值的变化
@interface GHConfigDeviceManager : NSObject
@property (nonatomic) GHNetworkStatus networkStatus;
[[[RACObserve(GHConfigDeviceManager.share, networkStatus) skip:1] distinctUntilChanged] subscribeNext:^(id _Nullable x) {
NSLog(@"networkStatus----x=%@",x);
if ([x intValue] == 2) {
if (weakself.alertView) {
[weakself.alertView dismiss:nil];
}
}
}];
3).监听textfeild文字变化
实时监测输入文字变化
[[alertView.inputTextField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
//昵称的长度范围1~20个字符
[weakalertView.okBtn setTitleColor:(weakalertView.inputTextField.text.length == 0 || weakalertView.inputTextField.text.length > 20) ? ASColorHex(0xC1CCC9) : ASColorHex(0x0BD087) forState:UIControlStateNormal];
weakalertView.okBtn.enabled = (x.length == 0 || x.length > 20) ? NO : YES;
}];
4).监听通知回调
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIApplicationDidBecomeActiveNotification object:nil] subscribeNext:^(NSNotification * _Nullable x) {
NSLog(@"x===%@",x);
}];
5).手势执行方法监听
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
self.lable.userInteractionEnabled = YES;
[self.lable addGestureRecognizer:tap];
[[tap rac_gestureSignal] subscribeNext:^(__kindof UIGestureRecognizer * _Nullable x) {
}];
6).数组与字典遍历
数组遍历
NSArray *array = @[@"111",@"222",@"333",@"444"];
[array.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"x11====%@",x);
}];
2021-05-27 11:22:41.734488+0800 GHome[5071:1101120] x11====222
2021-05-27 11:22:41.734570+0800 GHome[5071:1101120] x11====333
2021-05-27 11:22:41.734634+0800 GHome[5071:1101120] x11====444
字典遍历
NSDictionary *dict = @{@"111":@"-111",@"222":@"-222",@"333":@"-333",@"444":@"-444"};
[dict.rac_sequence.signal subscribeNext:^(id _Nullable x) {
NSLog(@"x22====%@",x);
}];
以下为打印内容,RACTwoTuple可视为二维数组类型
2021-05-27 11:19:24.804544+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c09f0> (
222,
"-222"
)
2021-05-27 11:19:24.804825+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0bb0> (
111,
"-111"
)
2021-05-27 11:19:24.804941+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0bc0> (
444,
"-444"
)
2021-05-27 11:19:24.805051+0800 GHome[5064:1099660] x22====<RACTwoTuple: 0x2834c0be0> (
333,
"-333"
)
三 .常用RAC高阶函数
1).信号合并combineLastest
RACSignal *accountSignal = self.accountTextField.rac_textSignal;
RACSignal *passwordSignal = self.passwordTextField.rac_textSignal;
RAC(self, loginBtn.enabled) = [RACSignal combineLatest:@[accountSignal, passwordSignal] reduce:^id(NSString *account, NSString *password){
@strongify(self);
BOOL b;
b = [account isValidatePhoneNumber];
return @(b && [password isValidatePassword]);
}];
account为accountSignal信号的回调值,password为passwordSignal信号的回调值,我们在reduce回调函数里面处理条件判断,并将判断结果以信号的形式返回给loginBtn.enabled的值
2).map的使用
一句话概括map的作用:主要用于数据的再封装与改造
例1 监听code_version的值变化,并字符串格式化,最后赋值到accessoryTitle属性
RAC(self, accessoryTitle) = [RACObserve(self, deviceViewModel.code_version) map:^id _Nullable(NSString *_Nullable value) {
return ASStringFormat(@"V%@", value);
}];
例2 遍历value数组并将value中元素重新组装生成GHDeviceSettingModel值,然后重新生成数组array返回
[value.rac_sequence map:^GHDeviceSettingViewModel * _Nullable(NSDictionary *_Nullable value) {
@strongify(self);
GHDeviceSettingModel *model = [GHDeviceSettingModel mj_objectWithKeyValues:value];
return [[GHDeviceSettingViewModel alloc] initWithSettingModel:model deviceViewModel:self.deviceViewModel];
}].array
3). filter的使用
例1 只有在textfeild输入框中的内容少于六位的时候执行回调
self.textField.rac_textSignal filter: BOOL (NSString *_ Nullable value) {
if (self.textField.text.length>6) f
self.textField.text = [self.textField.text substringToIndex:6];
}
return value.length<6;
}] subscribeNext:(NSString *_ Nullable x){
NSLog(@"filter----%@",x);
];
filter经常情况伴随着sequence(遍历)一起使用
例2
遍历数组dataSource的值,根据需求添加过滤条件,当return YES时会添加到返回的数组array中,return NO时不会添加到数组array中
NSArray *tempArray = [[self.OTAViewModel.dataSource rac_sequence] filter:^BOOL(GHOTACellViewModel* _Nullable value) {
if (value.type.intValue == 2) {
return YES;
}
return NO;
}].array;
使用rac进行遍历的好处就是,遍历过程中不用重新创建Array或Dictionary,即可拿到新生成的Array或Dictionary
4).flattenMap 映射
例 对输入内容进行二次封装处理
[[self.textField.rac_textSignal flattenMap:__kindof RACSignal *_ Nullable(NSString *
_Nullable value) {
NSLog( @"%@",value);//+8610086
return [RACReturnSignal return:[NSString stringWithFormat:@"+86%@",value]];
}] subscribeNext:^(id _Nullable х) {
NSLog(@"映射: %@",x);
}];
三.结合MVVM+RAC的简单使用
这里简单介绍一下mvvm
众所周知MVC模式具有厚重的ViewController、遗失的网络逻辑(没有属于它的位置)、较差的可测试性等问题。因此也就会有维护性较强、耦合性很低的一种新架构MVVM (MVC 引申出得新的架构)的流行。
主要在于 ViewModel
ViewModel: 相比较于MVC新引入的视图模型。是视图显示逻辑、验证逻辑、网络请求等代码存放的地方,唯一要注意的是,任何视图本身的引用都不应该放在VM中,换句话说就是VM中不要引入UIKit.h (对于image这个,也有人将其看做数据来处理,这就看个人想法了,并不影响整体的架构)。
比如一个用户登录网络请求,将网络请求相关逻辑都放到viewModel中执行
@interface GHLoginViewModel : NSObject
@property (nonatomic, strong) RACCommand *loginCommand;
@property (nonatomic, strong) RACCommand *refreshTokenCommand;
@interface GHLoginRequest : GHNetworkBaseRequest
/// 手机号\邮箱
@property (nonatomic, copy) NSString *username;
/// 密码(密码由 8 - 128 字符组成,不能为纯数字或字母)
@property (nonatomic, copy) NSString *password;
/// 国家码简称
@property (nonatomic, copy) NSString *region_code;
/// 手机号国家码
@property (nonatomic, copy) NSNumber *phone_code;
@end
@implementation GHLoginViewModel
- (instancetype)init
{
self = [super init];
if (self) {
}
return self;
}
- (RACCommand *)loginCommand {
if (!_loginCommand) {
_loginCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(GHLoginRequest* _Nullable input) {
return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
NSString *taskId = [GHNetworkModule.share sendRequest:input cacheComplete:nil networkComplete:^(GHNetworkResponse *response) {
if (response.status == GHNetworkResponseStatusSuccess) {
GHUserInfo.share.password = input.password;
GHUserInfo.share.token = response.data[@"token"];
[subscriber sendNext:response.data];
[subscriber sendCompleted];
[GHUserInfo cacheInfo];
} else {
[subscriber sendError:response.error];
}
}];
return [RACDisposable disposableWithBlock:^{
[GHNetworkModule.share cancelRequestWithRequestID:taskId];
}];
}];
}];
}
return _loginCommand;
}
在viewcontoller中接收请求结果
//返回数据处理
[self.viewModel.loginCommand.executionSignals.switchToLatest subscribeNext:^(id _Nullable x) {
@strongify(self)
[GHHudTip hideHUDWithView:self.view];
}];
//异常处理
[self.viewModel.loginCommand.errors subscribeNext:^(NSError * _Nullable x) {
@strongify(self)
[GHHudTip hideHUDWithView:self.view];
[GHHudTip showTips:x.domain];
}];
延伸与扩展:iOS MVVM+RAC 从框架到实战
结语:RAC的功能非常强大,且实用。这里只是列举了一小部分。其他还需要我们在开发中慢慢发掘。加油~
网友评论