美文网首页
RactiveCocoa

RactiveCocoa

作者: 为什么划船不靠桨 | 来源:发表于2018-07-17 18:52 被阅读0次

    RAC 指的就是 RactiveCocoa ,基于函数式响应式编程思想的Objective-C实践,是 Github 的一个开源框架,能够帮我们提供大量方便的事件处理方案,让我们更简单粗暴地去处理事件.使用MVVM框架就不得不提RAC.RAC具有高聚合低耦合的思想,使用RAC会让代码更简洁,逻辑更清晰。
    RAC几乎接管了Apple所有的事件机制,由于RAC将Cocoa中KVO、UIKitEvent、delegate、selector等都增加了RAC支持,所以都不用去做很多跨函数的事。

    ReactiveCocoa主要包含四个组件:
    信号源:RACStream 及其子类;
    订阅者:RACSubscriber 的实现类及其子类;
    调度器:RACScheduler 及其子类;
    清洁工:RACDisposable 及其子类。
    而信号源是最核心的部分,其它所有组件都是围绕它运作的。
    RAC 的核心思想:创建信号 - 订阅信号 - 发送信号

    1. RACSignal 信号类

    表示将来有数据传递,有数据改变,信号内部接收到数据,就会马上发出数据,外部就可以接收到数据了。默认信号都是冷信号,就是这个值改变了它不会触发,只有订阅(调用信号RACSignal的subscribeNext订阅)了这个信号,这个信号才会变为热信号(值一改变就触发),才会触发。

    /* 创建信号 */
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            //RACSubscriber订阅者对象,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。
            /* 发送信号 */
            [subscriber sendNext:@"发送信号"];
            
            return [RACDisposable disposableWithBlock:^{
                // 当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号。
                // 执行完Block后,当前信号就不在被订阅了。
                NSLog(@"dealloc");
            }];
            
            //return nil;
        }];
       //接收信号
        [signal subscribeNext:^(id  _Nullable x) {
            
        }];
    

    也可以使用RACDisposable类直接取消订阅

    /* 订阅信号 */
        RACDisposable *disposable = [signal subscribeNext:^(id  _Nullable x) {
            //RACDisposable用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。 当你不想监听某个信号时,可以通过它主动取消订阅信号。
            NSLog(@"信号内容:%@", x);
        }];
        
        /* 取消订阅 */
        [disposable dispose];
    
    2. 定时器timer(在子线程执行)
        [[RACSignal interval:1.0 onScheduler:[RACScheduler scheduler]] subscribeNext:^(NSDate * _Nullable x) {
            NSLog(@"当前时间:%@", x); // x 是当前的系统时间
            //关闭计时器就是取消订阅
            //[disposable dispose];
        }];
    
    3. RACSubject 信号提供者

    RACSubject自己可以充当信号,又能发送信号。(和代理的用法类似,通常用来代替代理,有了它,就不必定义代理了)

    /* 创建信号 */
        RACSubject *subject = [RACSubject subject];
        
        /* 订阅信号(通常在别的视图控制器中订阅,与代理的用法类似) */
        [subject subscribeNext:^(id  _Nullable x) {
            /*
             1创建订阅者对象
             2将block放到订阅者对象中
             3将订阅者对象放到subscribes数组里面
             */
            NSLog(@"信号内容:%@", x);
        }];
        
        /* 发送信号 */
        /*
         其内部的真实操作是遍历信号对象中的数组,取出订阅者,调用订阅者中的block执行
         */
        [subject sendNext:@"发送信号"];
    
    4. RACTuple 元祖(和 OC 的数组其实是一样的,其实就是封装了我们 OC 的数组)
    /* 创建元祖 */
        RACTuple *tuple1 = [RACTuple tupleWithObjects:@"1", @"2", @"3", @"4", @"5", nil];
        
        /* 从别的数组中获取内容 */
        RACTuple *tuple2 = [RACTuple tupleWithObjectsFromArray:@[@"1", @"2", @"3", @"4", @"5"]];
        
        /* 利用 RAC 宏快速封装 */
        RACTuple *tuple3 = RACTuplePack(@"1", @"2", @"3", @"4", @"5");
        
        NSLog(@"取元祖内容:%@", tuple1[0]);
        NSLog(@"第一个元素:%@", [tuple2 first]);
        NSLog(@"最后一个元素:%@", [tuple3 last]);
    

    这里再说一下RACSequence这个集合类,用于代替NSArray, NSDictionary,可以使用它来快速遍历数组和字典。

    //遍历数组
        /*
         遍历原理:
         通过arr.rac_sequence把数据arr转化成集合RACSequence
         通过arr.rac_sequence.signal把集合RACSequence转化成了信号
         *通过subscribeNext订阅信号,遍历集合
         */
        NSArray *array = @[@"1", @"2", @"3", @"4", @"5"];
        [array.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"数组内容:%@", x); // x 可以是任何对象
        }];
    
    
        /* 遍历字典 */
        //遍历字典,遍历出来的键值对会包装成RACTuple(元组对象)
        NSDictionary *dictionary = @{@"key1":@"value1", @"key2":@"value2", @"key3":@"value3"};
        [dictionary.rac_sequence.signal subscribeNext:^(RACTuple * _Nullable x) {
           //RACTupleUnpack是一个宏定义
            RACTupleUnpack(NSString *key, NSString *value) = x; // x 是一个元祖,这个宏能够将 key 和 value 拆开
            NSLog(@"字典内容:%@:%@", key, value);
        }];
    
    
    //下面两个方法都是将数组内容全部换为 0 ,第一个是单个操作,第二个是一次性全部替换,两个方法都不会改变原数组内容,操作完后都会生成一个新的数组,省去了创建可变数组然后遍历出来单个添加的步骤。
        /* 内容操作 */
        NSArray *array1 = @[@"1", @"2", @"3", @"4", @"5"];
        NSArray *newArray1 = [[array1.rac_sequence map:^id _Nullable(id  _Nullable value) {
            
            NSLog(@"数组内容:%@", value);
            
            return @"0"; // 将所有内容替换为 0
            
        }] array];
    
    
        /* 内容快速替换 */
        NSArray *array2 = @[@"1", @"2", @"3", @"4", @"5"];
        NSArray *newArray2 = [[array.rac_sequence mapReplace:@"0"] array]; // 将所有内容替换为 0
    
    5. 代理
        UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"标题" message:@"123456" delegate:self cancelButtonTitle:@"cancel" otherButtonTitles:@"other", nil];
    //可以省去监听以及设置 delegate 的步骤
        [[self rac_signalForSelector:@selector(alertView:clickedButtonAtIndex:) fromProtocol:@protocol(UIAlertViewDelegate)] subscribeNext:^(RACTuple *tuple) {
            NSLog(@"%@",tuple.first);
            NSLog(@"%@",tuple.second);
            NSLog(@"%@",tuple.third);
        }];
        [alertView show];
    
    6. 通知
    //RAC将收到通知后的方法封装成了block,省去了在 dealloc 中清除通知和监听通知创建方法的步骤。
        [[[NSNotificationCenter defaultCenter] rac_addObserverForName:UIKeyboardWillShowNotification object:nil] subscribeNext:^(NSNotification *notification) {
            NSLog(@"%@", notification.name);
            NSLog(@"%@", notification.object);
        }];
    
    7. KVO
        UIScrollView *scrolView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, 200, 200)];
        scrolView.contentSize = CGSizeMake(200, 800);
        scrolView.backgroundColor = [UIColor redColor];
        [self.view addSubview:scrolView];
    
        //原生,其缺点是当你需要观察很多属性时,你要写很多次下面的方法,而且在观察的方法中还要去判断KeyPath具体是什么
        [scrolView addObserver:self forKeyPath:@"frame" options:NSKeyValueObservingOptionNew context:nil];
    
        //RAC方式1  利用 RAC 的宏,只要对象的属性发生改变就会产生信号
        [RACObserve(scrolView, contentOffset) subscribeNext:^(id x) {
            NSLog(@"contentOffset: %@",x);
        }];
        //RAC方式2
        [self.redView rac_observeKeyPath:@"frame" options:NSKeyValueObservingOptionNew observer:self block:^(id value, NSDictionary *change, BOOL causedByDealloc, BOOL affectedOnlyLastComponent) {
            
        }];
        //RAC方式3    先包装成了一个信号,只要值改变就会发送信号。
        [[self.redView rac_valuesForKeyPath:@"frame" observer:self] subscribeNext:^(id  _Nullable x) {
            NSLog(@"frame属性的改变1:%@", x); // x 是监听属性的改变结果
        }];
    
    8. 监听 TextField 的输入改变
    //可以省去设置 delegate 和实现代理方法的步骤。
        [[self.textField.rac_textSignal filter:^BOOL(NSString * _Nullable value) {
            
            return value.length > 5; // 表示输入文字长度 > 5 时才会调用下面的 block
            
        }] subscribeNext:^(NSString * _Nullable x) {
            
            NSLog(@"输入框内容:%@", x);
        }];
        
        [[self.textField.rac_textSignal map:^id(id value) {
            return [UIColor redColor];
        }] subscribeNext:^(id x) {
            //        NSLog(@"x:%@", x);
        }];
        
        
        [self.textField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {
            NSLog(@"输入框内容:%@", x);
        }];
        
        //给label同时赋值,其实就是用绑定信号的,只要产生信号内容,就会把内容给属性赋值
        UILabel *label;
        RAC(label,text) = self.textField.rac_textSignal;
    
    9. 添加手势
    //UIView
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc]init];
        [tap.rac_gestureSignal subscribeNext:^(id x) {
            NSLog(@"x:%@", x);
            self.redView.backgroundColor = [UIColor yellowColor];
        }];
        [self.redView addGestureRecognizer: tap];
    
    10. 监听 Button 点击事件
    //可以省去 addTarget 添加事件和创建方法的步骤。
        [[self.btn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {
            NSLog(@"点击了按钮");
        }];
    
    
        //登录按钮状态实时监听
        //下面表示只有 用户名 和 密码 输入框内容都大于 0 时,登录 按钮才可以点击,而且状态是实时监听的,一句代码就能完成这个功能。
        RAC(self.btn, enabled) = [RACSignal combineLatest:@[self.textField.rac_textSignal, self.textField.rac_textSignal] reduce:^id _Nullable(NSString * username, NSString * password){
            
            return @(username.length && password.length);
        }];
    

    相关文章

      网友评论

          本文标题:RactiveCocoa

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