美文网首页IOS开发RAC运用精选集
iOS-ReactiveObjC(RAC) 学习笔记

iOS-ReactiveObjC(RAC) 学习笔记

作者: 9岁就很6 | 来源:发表于2019-03-07 15:19 被阅读0次

    RAC简介

    • 在RAC中,万物皆信号。
    • RAC 指的就是 RactiveCocoa ,是 Github 的一个开源框架,能够通过信号提供大量方便的事件处理方案,让我们更简单粗暴地去处理事件,现在分为 ReactiveObjC(OC) 和 ReactiveSwift(swift),官方的说,ReactiveCocoa(其简称为RAC)是由GitHub开源的一个应用于iOS和OS X开发的新框架。RAC具有函数式编程响应式编程的特性。。
    • 团队协作时,必须注意一个点,对于很熟悉RAC的人来说,使用RAC是非常方便的。但对于不熟悉RAC的人来说,由于RAC的可阅读性是很差的,所以需耗费大量时间阅读和学习。
    • 未避免循环引用,需使用@weakify(self),@strongify(self)。这两个宏至少是一对出现的
    • RAC就是一个第三方库,他可以大大简化你的代码过程。

    RAC架构框架图


    RAC架构框架图 信号流程

    一 、使用

    老规矩,pod install

    pod 'ReactiveObjC', '~> 3.0.0' //建议纯oc代码使用ReactiveObjC
    

    1、基本控件

    • UITextField
    //监听文本输入
    [[_textField rac_textSignal] subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"%@",x);
    }];
    //可根据自己想要监听的事件选择
    [[_textField rac_signalForControlEvents:UIControlEventEditingChanged] subscribeNext:^(__kindof UIControl * _Nullable x) {
            NSLog(@"%@",x);
        }];
    //添加条件 --  下面表示输入文字长度 > 10 时才会调用subscribeNext
    [[_textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) { 
        return value.length > 10;
    }] subscribeNext:^(NSString * _Nullable x) {
         NSLog(@"输入框内容:%@", x);
    }];
    
    • UIButton
       //监听按钮点击事件
      [[View.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
            NSLog(@"-->%@",x);
        }];
    
    //这种不建议使用,暂时不知道didBtnAction方法不写会报警高~不过可以也实现
    //    [[View.btn rac_signalForSelector:@selector(didBtnAction:)]subscribeNext:^(id x) {
    //       NSLog(@"-->%@",x);
    //    } completed:^{
    //    }];
    

    rac 类似代理传值例子

     //第一
    @property (strong,nonatomic) RACSubject *btnSubject;
    
    //第二,在View点m,按钮点击方法里面 
        // view里面带有btn,点击的时候 
    
    - (void)didBtnAction:(UIButton *)btn{
        SWLog(@"回到控制器后g要干嘛?");
       
        [self.btnSubject sendNext:@{@"text":@"这边可以携带参数"}];
        
    }
    
    - (RACSubject *)btnSubject{
        if (!_btnSubject) {
            _btnSubject = [RACSubject subject];
        }
        return _btnSubject;
    }
    
    //控制器外部调用
        [View.btnSubject subscribeNext:^(id x) {
            SWLog(@"收到!==%@",x);
           // log==> @{@"text":@"这边可以携带参数"}
        } completed:^{
            
        }];
    
    • 计时器(interval、delay)
    
        /*
         声明:RACDisposable *disposable
         创建一个RACDisposable,调用disposable方法等时候就会进入创建对象的block,把定时器释放掉
         这两个宏就是为了解决循环引用的,且必须配套使用。
         @weakify(self)
         @strongify(self)
         
         相当于下面:
         __weak typeof(self) weakSelf = self;
         __strong typeof(weakSelf) strongSelf = weakSelf;
         */
        
    
    //类似timer
    @weakify(self)
    self.disposable = [[RACSignal interval:2 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
            @strongify(self)
            NSLog(@"时间:%@", x); // x 是当前的时间
            //关闭计时器
            [self.disposable dispose];
    }];
    //延时
    [[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            [subscriber sendNext:@"延时2秒"];
            return nil;
        }] delay:2] subscribeNext:^(id x) {
            
            NSLog(@"-->%@",x);
        }];
    
    

    2.KVO 属性监听

    //方法一
    [_redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:nil block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
           NSLog(@"方法一 %@",value);
    }];
    //方法二
    [[_redView rac_valuesForKeyPath:@"frame" observer:nil] subscribeNext:^(id  _Nullable x) {
            NSLog(@"方法二 %@",x);
    }];
    //方法三 宏写法
    [RACObserve(_redView, frame) subscribeNext:^(id  _Nullable x) {
            NSLog(@"方法三%@",x);
        }];
    

    3.textField监听,lable赋值,手势

    //此处RAC宏相当于让_label订阅了_textField的文本变化信号
    //赋值给label的text属性
    RAC(_label, text) = _textField.rac_textSignal;
    
     //快速添加手势
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
        [[tap rac_gestureSignal] subscribeNext:^(id x) {
        } completed:^{
        }];
        [newTextLable addGestureRecognizer:tap];
    
    //输入框监听
    [[self.textFild rac_textSignal] subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
    

    4.监听 Notification 通知事件

        //通知常用
        [[[NSNotificationCenter defaultCenter] rac_addObserverForName:@"通知名字" object:nil]subscribeNext:^(id x) {
            
        } completed:^{
            
        }];
    

    5.多个订阅 RACMulticastConnection

    - (void)sss{
        
        RACSignal *signal=[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"123444"];
            return nil;
        }];
        RACMulticastConnection *mut = [signal publish];
        [mut.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        [mut.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"22%@",x);
        }];
        [mut connect];
    }
    
    

    6.绑定信号 bind

    - (void)text{
      RACSubject *signal = [RACSubject subject];
      [[signal bind:^RACStreamBindBlock _Nonnull{
         return ^RACSignal *(id value, BOOL* stop){
             NSLog(@"%@",value);
              NSString *v = [NSString stringWithFormat:@"处理过的数据%@",value];
              return [RACReturnSignal return:v];//需要导入头文件#import "RACReturnSignal.h"
            };
      }] subscribeNext:^(id  _Nullable x) {
         NSLog(@"%@",x);
        }];
        [signal sendNext:@"1111"];
        //输出
        //1111
        //处理过的数据1111
    
    }
    

    7.映射

      - (void)text{  
        //映射
        RACSubject *subject = [RACSubject subject];
        [[subject map:^id _Nullable(id  _Nullable value) {
            return [NSString stringWithFormat:@"处理过的数据%@",value];
            
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        } ];
        [subject sendNext:@"11"];
        
        //输出
        //处理过的数据11
    }
    

    8.忽略 ignore

    - (void)text2{
        RACSubject * subject = [RACSubject subject];
        [[subject ignore:@"a"] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        
        [subject sendNext:@"a"];
        [subject sendNext:@"b"];
        [subject sendNext:@"c"];
        
        
        //输出log
    //    2019-03-07 14:35:00.348048+0800 MVVM+RAC[10199:763418] b
    //    2019-03-07 14:35:00.348200+0800 MVVM+RAC[10199:763418] c
    }
    

    9 RACTuple、RACSequence

    1.RACTuple元组类,类似NSArray,用来包装值.
    2.RACSequence:RAC中的集合类,用于代替NSArray,NSDictionary,可以使用它来快速遍历数组和字典。

      // 1.遍历数组
        NSArray *numbers = @[@1,@2,@3,@4];
     
        // 这里其实是三步
        // 第一步: 把数组转换成集合RACSequence numbers.rac_sequence
        // 第二步: 把集合RACSequence转换RACSignal信号类,numbers.rac_sequence.signal
        // 第三步: 订阅信号,激活信号,会自动把集合中的所有值,遍历出来。
        [numbers.rac_sequence.signal subscribeNext:^(id x) {
     
            NSLog(@"%@",x);
        }];
     
     
        // 2.遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)
        NSDictionary *dict = @{@"name":@"xmg",@"age":@18};
        [dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
     
            // 解包元组,会把元组的值,按顺序给参数里面的变量赋值
            RACTupleUnpack(NSString *key,NSString *value) = x;
     
            // 相当于以下写法
    //        NSString *key = x[0];
    //        NSString *value = x[1];
     
            NSLog(@"%@ %@",key,value);
     
        }];
     
     
        // 3.字典转模型
        // 3.1 OC写法
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
     
        NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
     
        NSMutableArray *items = [NSMutableArray array];
     
        for (NSDictionary *dict in dictArr) {
            FlagItem *item = [FlagItem flagWithDict:dict];
            [items addObject:item];
        }
     
        // 3.2 RAC写法
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
     
        NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
     
        NSMutableArray *flags = [NSMutableArray array];
     
        _flags = flags;
     
        // rac_sequence注意点:调用subscribeNext,并不会马上执行nextBlock,而是会等一会。
        [dictArr.rac_sequence.signal subscribeNext:^(id x) {
            // 运用RAC遍历字典,x:字典
     
            FlagItem *item = [FlagItem flagWithDict:x];
     
            [flags addObject:item];
     
        }];
     
        NSLog(@"%@",  NSStringFromCGRect([UIScreen mainScreen].bounds));
     
     
        // 3.3 RAC高级写法:
        NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
     
        NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath];
        // map:映射的意思,目的:把原始值value映射成一个新值
        // array: 把集合转换成数组
        // 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。
        NSArray *flags = [[dictArr.rac_sequence map:^id(id value) {
     
            return [FlagItem flagWithDict:value];
     
        }] array];
    
    

    10 项目开发- RACCommand

    RACCommand:RAC中用于处理事件的类,可以把事件如何处理,事件中的数据如何传递,包装到这个类中,他可以很方便的监控事件的执行过程。

    模拟实际开发中,一个获取书架的接口传参数和请求:
    1.在viewModel.h 里面声明一个属性
    /// 获取书架
    @property (nonatomic , strong) RACCommand *get_list_bookcaseCommand;
    
    2.在viewModel.m 进行懒加载实现
    #pragma mark - get
    - (RACCommand *)get_list_bookcaseCommand
    {
        if (_get_list_bookcaseCommand== nil) {
            kWeakObject(self)
            _get_list_bookcaseCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
                return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                    kStrongObject(self)
                   [CPNetManager POST:get_list_bookcaseUrl parameters:input success:^(NSDictionary * _Nullable responseObject, NSURLSessionDataTask * _Nonnull task) { 
                        [subscriber sendNext:responseObject];
                        [subscriber sendCompleted];
                    } failure:^(NSDictionary * _Nullable responseObject, NSString * _Nullable message, NSString * _Nullable code, NSURLSessionDataTask * _Nonnull task) {
                        [subscriber sendNext:nil];
                        [subscriber sendCompleted];
                    }]; 
                    return nll;
                }];
                
            }];
        }
        return _get_list_bookcaseCommand;
    }
    
    3.在controller 进行懒加载viewModel对象再需要请求的作用域去调用传参数和信用监听。
    //self.viewModel.get_list_bookcaseCommand  为RACCommand对象 发送参数进行请求,input为参数
     [self.viewModel.get_list_bookcaseCommand execute:@{@"page":@"1",@"uid”: userModel.uid}];
    
     //获取书架 [self.viewModel.get_list_bookcaseCommand.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
            kStrongObject(self)
            [self.viewModel.headRefresh endRefreshing];
            [self.viewModel.footRefresh endRefreshing];
            if (x)
            {
    //收到数据x 进行解析
                self.dataModel.model = [HBHomeModel yy_modelWithJSON:x[@"data"]];
                [self.viewModel.collectionView reloadData];
                [self.viewModel.footRefresh endRefreshingWithNoMoreData];
            }
        }]; 
     
    

    11、ReactiveCocoa开发中常见用法。

    11.1 代替代理:
    rac_signalForSelector:用于替代代理。

    11.2 代替KVO :
    rac_valuesAndChangesForKeyPath:用于监听某个对象的属性改变。

    11.3 监听事件:
    rac_signalForControlEvents:用于监听某个事件。

    11.4 代替通知:
    rac_addObserverForName:用于监听某个通知。

    11.5 监听文本框文字改变:
    rac_textSignal:只要文本框发出改变就会发出这个信号。

    11.6 处理当界面有多次请求时,需要都获取到数据时,才能展示界面
    rac_liftSelector:withSignalsFromArray:Signals:当传入的Signals(信号数组),每一个signal都至少sendNext过一次,就会去触发第一个selector参数的方法。
    使用注意:几个信号,参数一的方法就几个参数,每个参数对应信号发出的数据。

    12 、项目开发-短信倒计时、登录按钮是否可点击

    @property (nonatomic, strong) RACDisposable * disposable;
    
    @property (nonatomic, assign) NSInteger time;
    
     @weakify(self)
        [[_sendButton rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
           @strongify(self)
            self.time = 10;
            self.disposable = [[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]] subscribeNext:^(NSDate * _Nullable x) {
                @strongify(self)
                self.time -- ;
                NSString *text = (self.time > 0) ? [NSString stringWithFormat:@"请稍等%zd秒",self.time] : @"重新发送";
                [self.sendButton setTitle:text forState:UIControlStateNormal];
                self.sendButton.enabled = (self.time == 0);
                if (self.time == 0)   [self.disposable dispose];  //关掉信号
            }];
             
        }];
    
      //根据textfield的内容来改变登录按钮的点击可否
        RAC(loginBtn, enabled) = [RACSignal combineLatest:@[userNameTF.rac_textSignal, passwordTF.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
            return @(username.length >= 11 && password.length >= 6);
        }];
        //根据textfield的内容来改变登录按钮的背景色
        RAC(loginBtn, backgroundColor) = [RACSignal combineLatest:@[userNameTF.rac_textSignal, passwordTF.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
            return (username.length >= 11 && password.length >= 6) ? [UIColor redColor] : [UIColor grayColor];
        }];
    
    

    总结 以上就是RAC的用法,希望对大家有帮助。

    相关文章

      网友评论

        本文标题:iOS-ReactiveObjC(RAC) 学习笔记

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