美文网首页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实践

    前言 RAC使用-->IOS RAC使用 -- ReactiveObjC 本文使用RAC+MVVM来模拟用户登录 ...

  • RAC iOS

    使用RAC 1.target-action RAC最基本的入门使用技巧就是对事件的监听。 PS:在iOS开发中,我...

  • ReactiveCocoa详解

    一、RAC介绍 RAC 是一个 iOS 中的函数式响应式编程框架,一般与MVVM配套使用。在非RAC开发中,都是习...

  • iOS中RAC的使用

    Reactive Cocoa(RAC)是一个支持FRP(函数响应式编程)的框架;详细的就不再多介绍了,直接说说在我...

  • iOS中的RAC的使用

    本篇内容主要记录一下平时项目中经常会用到的rac的情况,不做太深入的研究。 前言 ReactiveCocoa 可以...

  • RAC简介

    前言 RAC是iOS开发中经常使用到响应式框架,在OC中可以通过pod导入 RAC信号创建与发送 RAC中一个常见...

  • iOS RAC学习

    一、RAC使用基础认知 RAC编程思想:响应式编程+函数式编程 在iOS中,类之间的传值,无非就是block、de...

  • iOS RAC常见问题汇总

    简介:本文主要对RAC使用过程中遇到的问题进行汇总,方便后续查询和学习。《iOS RAC常用方法汇总》[https...

  • RAC

    IOS响应式编程框架ReactiveCocoa(RAC)使用示例 感觉RAC响应起来太乱,不太实用,但是平时自己的...

  • (一)、iOS RAC - RACSignal

    (一)、iOS RAC - RACSignal (二)、iOS RAC - RACDisposable(三...

网友评论

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

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