美文网首页iOS常用iOSiOS
iOS中的RAC的使用

iOS中的RAC的使用

作者: 文小猿666 | 来源:发表于2021-01-20 18:08 被阅读0次

    本篇内容主要记录一下平时项目中经常会用到的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 引申出得新的架构)的流行。

    图片.png

    主要在于 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的功能非常强大,且实用。这里只是列举了一小部分。其他还需要我们在开发中慢慢发掘。加油~

    相关文章

      网友评论

        本文标题:iOS中的RAC的使用

        本文链接:https://www.haomeiwen.com/subject/oblqzktx.html