• RAC(ReactiveCocoa)介绍(一)


    最近在学习RAC,之前在iOS工作中,类之间的传值,无非是block、delegate代理、KVO和Notification等这几种方法。在RAC中,同样具备替代block、delegate代理、KVO和Notification,UI target、定时器timer、数据结构等各种方式。依靠FRP(响应式函数编程),RAC方法本身更加简单明了,通过提供信号的方式(RACSignal)可以捕捉到当前以及未来的属性值的变化,而且无需持续观察和更新代码。可直接在block中将逻辑代码加入其中,使得代码紧凑,更加直观。

    先来介绍ObjC版本,使用cocoaPods在podfile中添加 pod 'ReactiveObjC', '~> 3.1.0' ,然后pod install一下。在项目中#import <ReactiveObjC.h>,建议放入pch头文件中。

    通过RAC提供的方法与系统提供的方法分别进行对比,先来感受下RAC的强大之处

    一、UIButton

    1.1 传统方式

    - (UIButton *)testBtn{
        if (!_testBtn) {
            _testBtn = [UIButton buttonWithType:UIButtonTypeCustom];
            _testBtn.backgroundColor = [UIColor redColor];
            _testBtn.frame = CGRectMake((UIScreen.mainScreen.bounds.size.width - 60)/2, (UIScreen.mainScreen.bounds.size.height - 60)/2, 60, 60);
            [_testBtn addTarget:self action:@selector(tapAction) forControlEvents:UIControlEventTouchUpInside];
        }
        return _testBtn;
    }
    
    - (void)tapAction{
        NSLog(@"你好");
    }

    运行结果为

    1.2 RAC

    - (void)btnRAC{
        [[self.testBtn rac_signalForControlEvents:UIControlEventTouchUpInside]subscribeNext:^(__kindof UIControl * _Nullable x) {
            NSLog(@"RAC按钮点击了");
        }];
    }

    运行结果如下:

    二、KVO

    2.1 传统KVO

    KVO在使用时,必须在- (void)observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void*)context中实现针对KVO监听属性值变化的处理,而且对于KeyPath书写容易产生手写错误。在对应类dealloc时,KVO还必须要进行remove操作,否则会程序crash。
    - (void)KVO{
        [self orignKVO];
        [self RACKVO];
    }
    
    - (void)orignKVO{
        [self.testLabel addObserver:self forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
    }
    
    - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
        if ([keyPath isEqualToString:@"text"] && object == self.testLabel) {
            NSLog(@"%@",change);
        }
    }

    2.2 RAC版

    在使用RAC代替KVO时,不仅能大大增加代码可读性,而且RACObserve(<#TARGET#>, <#KEYPATH#>)宏定义中keyPath可以代码提示出target中的属性成员变量,降低手写代码错误的可能性。

    [RACObserve(self.testLabel, text)subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];

    三、delegate代理

    以UITextField为例,当需要对UITextField逻辑处理时,往往需要实现其各类代理方法,大大增加了代码量。当使用RAC之后

    - (void)KVODelegate{
        [[self rac_signalForSelector:@selector(textFieldDidBeginEditing:)fromProtocol:@protocol(UITextFieldDelegate)] subscribeNext:^(RACTuple * _Nullable x) {
            NSLog(@"%@",x);
        }];
        self.textField.delegate = self;
    }

    @selector方法选择器中键入要实现的代理方法,代理名称声明为对应的代理名称。block代码块中,当触发监听的代理方法时返回元组类型数据,与swift中的元组类型有所区别,此处的元组看起来更像是数组。

    四、Notification通知

    - (void)RACNotification{
        [[[NSNotificationCenter defaultCenter]rac_addObserverForName:UIKeyboardDidHideNotification object:nil]subscribeNext:^(NSNotification * _Nullable x) {
            NSLog(@"%@",x);
        }];
    }

    五、定时器timer

    - (void)RACTimer{
        //主线程每两秒执行一次
        [[RACSignal interval:2.0 onScheduler:[RACScheduler mainThreadScheduler]]subscribeNext:^(NSDate * _Nullable x) {
            NSLog(@"%@",x);
        }];
        
        //创建一个新线程
        [[RACSignal interval:1 onScheduler:[RACScheduler schedulerWithPriority:(RACSchedulerPriorityHigh)name:@"com.reactiveCocoa.RACScheduler.mainTreadScheduler"]]subscribeNext:^(NSDate * _Nullable x) {
            NSLog(@"%@",[NSThread currentThread]);
        }];
        
    }

    六、数组与字典

    遍历元素
    - (void)RACSequence{
        NSArray *array = @[@"乔布斯",@"苹果",@"发达"];
        [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        
        NSDictionary *dict = @{@"name":@"dragon",@"type":@"fire"};
        [dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            RACTwoTuple *tuple = (RACTwoTuple *)x;
            NSLog(@"key == %@, value == %@",tuple[0],tuple[1]);
        }];
    }

    七、RAC使用基本流程

    RAC基本使用方法与流程

    - (void)RACBaseUse{
        //RAC基本使用
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            [subscriber sendNext:@"sendOneMessage"];
            //发送error信号
            NSError *error = [NSError errorWithDomain:NSURLErrorDomain code:1001 userInfo:@{@"errorMsg":@"this is a error message"}];
            [subscriber sendError:error];
            
            //4. 销毁信号
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"signal已销毁");
            }];
        }];
        
        //2.1 订阅信号
        [signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@",x);
        }];
        
        //2.2 针对实际中可能出现的逻辑错误,RAC提供了订阅error信号
        [signal subscribeError:^(NSError * _Nullable error) {
            NSLog(@"%@",error);
        }];
    }

    以上代码中,subscribeNext作用为订阅信号,可在该block中输入逻辑相关代码块。

    注意问题,可能也会有循环引用问题产生,如下:

    - (void)RACFilter{
        @weakify(self);
        [[self.textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
            //过滤判断条件
            @strongify(self)
            if (self.textField.text.length >= 6) {
                self.textField.text = [self.textField.text substringToIndex:6];
                self.testLabel.text = @"已经到6位了";
                self.testLabel.textColor = [UIColor redColor];
            }
            return value.length <= 6;
            
        }] subscribeNext:^(NSString * _Nullable x) {
            //订阅逻辑区域
            NSLog(@"filter过滤后的订阅内容:%@",x);
        }];
    }

    以此来避免出现block的循环引用。

     稍后会在后续的文章里继续介绍如何使用,以及RAC信号流程原理。demo代码放到GitHub上。

    demo连接:https://github.com/zxy1829760/RAC-1-

  • 相关阅读:
    【搜索引擎】Solr最新安装以及通过关系型数据库(MySQL,Oracle,PostgreSQL)导入数据
    【搜索引擎】SOLR VS Elasticsearch(2019技术选型参考)
    【Java源码】集合类-优先队列PriorityQueue
    【Java源码】树-概述
    Go语言调度器之创建main goroutine(13)
    Go语言goroutine调度器初始化(12)
    Go语言goroutine调度器概述(11)
    线程本地存储及实现原理
    操作系统线程及线程调度
    系统调用
  • 原文地址:https://www.cnblogs.com/guohai-stronger/p/10442051.html
Copyright © 2020-2023  润新知