iOS中的RAC的使用

本篇内容主要记录一下平时项目中经常会用到的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的功能非常强大,且实用。这里只是列举了一小部分。其他还需要我们在开发中慢慢发掘。加油~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 夜莺2517阅读 127,695评论 1 9
  • 版本:ios 1.2.1 亮点: 1.app角标可以实时更新天气温度或选择空气质量,建议处女座就不要选了,不然老想...
    我就是沉沉阅读 6,863评论 1 6
  • 我是一名过去式的高三狗,很可悲,在这三年里我没有恋爱,看着同龄的小伙伴们一对儿一对儿的,我的心不好受。怎么说呢,高...
    小娘纸阅读 3,368评论 4 7
  • 这些日子就像是一天一天在倒计时 一想到他走了 心里就是说不出的滋味 从几个月前认识他开始 就意识到终究会发生的 只...
    栗子a阅读 1,610评论 1 3