RAC

作者: z4ywzrq | 来源:发表于2018-06-03 23:57 被阅读12次

本篇文章是用来记录一下有关 ReactiveCocoa 的学习笔记。

简介

ReactiveCocoa(简称 RAC)是由 Github 开源的一个应用于 iOS 和 macOS 开发的新框架。RAC具有函数式编程和响应式编程的特性,可称其为函数响应式编程(FRP, Functional Reactive Programming)框架。

“函数式编程”(programming paradigm)属于"结构化编程"的一种,主要思想是把运算过程尽量写成一系列嵌套的函数调用。
特点:
1、函数是"第一等公民":函数与其他数据类型一样,处于平等地位,可以赋值给其他变量,也可以作为参数,传入另一个函数,或者作为别的函数的返回值。
2、函数式编程要求,只使用表达式,不使用语句。也就是说,每一步都是单纯的运算,而且都有返回值。
3、函数的运行不依赖于外部变量或"状态",只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的。

例如,表达式(1 + 2) * 3 - 4,写成函数式语言:
subtract(multiply(add(1,2), 3), 4);
add(1,2).multiply(3).subtract(4);

“响应式编程”(Reactive Programming)是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。

例如,在命令式编程环境中,a = b + c 表示将表达式的结果赋给 a,而之后改变 b 或 c 的值不会影响 a,只是一个瞬时的过程。但在响应式编程中,a 的值会随着 b 或 c 的更新而更新。

函数响应式编程 FRP,正是在函数式编程的基础之上,增加了响应式的支持。FRP是基于异步事件流进行编程的一种编程范式。针对离散事件序列进行有效的封装,利用函数式编程的思想,满足响应式编程的需要。
RAC 是一个函数响应式编程框架,它给我们提供另外一套编码的思路与可能性,它能在宏观层面上提升代码易读性与稳定性。

RAC 介绍

下面来介绍一下 ReactiveObjC 框架,有关 Swift 版本框架的可查看 ReactiveCocoa or ReactiveSwift
在项目中导入框架:

开始之前先看看相关类图,如下:


ReactiveObjC

RAC 主要由以下核心组件构成:

  • 信号源:RACStream 及其子类;
  • 订阅者:RACSubscriber 的实现类及其子类;
  • 调度器:RACScheduler 及其子类;
  • 清洁工:RACDisposable 及其子类。
    其中,信号源又是最核心的部分,其他组件都是围绕它运作的。

对于一个应用来说,绝大部分的时间都是在等待某些事件的发生或响应某些状态的变化,比如用户的触摸事件、应用进入后台、网络请求成功刷新界面等等,而维护这些状态的变化,常常会使代码变得非常复杂,难以扩展。而 ReactiveCocoa 给出了一种非常好的解决方案,它使用信号来代表这些异步事件,提供了一种统一的方式来处理所有异步的行为,包括代理方法、block 回调、target-action 机制、通知、KVO 等:

//代理方法
[[self rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:) 
    fromProtocol:@protocol(UITableViewDelegate)] 
    subscribeNext:^(RACTuple * _Nullable x) {
    //实现代理方法
}];

//target-action
[[button rac_signalForControlEvents:UIControlEventTouchUpInside]
     subscribeNext:^(__kindof UIControl * _Nullable x) {
    //按钮点击
}];

//通知
[[[NSNotificationCenter defaultCenter] rac_addObserverForName:kNotificationName object:nil] 
    subscribeNext:^(NSNotification * _Nullable x) {
    //收到通知处理
}];

//KVO
[RACObserve(self, name) subscribeNext:^(id  _Nullable x) {
    //值改变处理
}];

1、RACStream

RACStream 是 ReactiveCocoa 中最核心的类,代表的是任意的值流,它是整个 RAC 得以建立的基础。
RACStream 是一个抽象类,通常情况下,我们并不会去实例化它,而是直接使用它的两个子类 RACSignal 和 RACSequence

RACSignal
        // 1.创建信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            // 3.发送信号
            [subscriber sendNext:@"message"];
            // 4.取消信号
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"取消订阅");
            }];
        }];
        // 2. 订阅信号
        RACDisposable *disposable = [signal subscribeNext:^(id  _Nullable x) {
            //只要信号内部发出数据就会调用这个block
            NSLog(@"x --- %@", x);
        }];
        // 取消订阅
        [disposable dispose];

RACSignal 有五个用来实现不同功能的私有子类:

  • RACEmptySignal :空信号,用来实现 RACSignal 的 +empty 方法;
  • RACReturnSignal :一元信号,用来实现 RACSignal 的 +return: 方法;
  • RACDynamicSignal :动态信号,使用一个 block 来实现订阅行为,我们在使用 RACSignal 的 +createSignal: 方法时创建的就是该类的实例;
  • RACErrorSignal :错误信号,用来实现 RACSignal 的 +error: 方法;
  • RACChannelTerminal :通道终端,代表 RACChannel 的一个终端,用来实现双向绑定。

RACSignal 的 subscribe 这个方法封装了订阅者对信号源的一次订阅过程,它是订阅者与信号源产生联系的唯一入口。

RACSubject
        //创建信号
        RACSubject *subject = [RACSubject subject];
        //订阅信号
        [subject subscribeNext:^(id  _Nullable x) {
            // block:处理数据
            NSLog(@"%@",x);
        }];
        //发送信号
        [subject sendNext:@1];
        

RACSubject 继承自 RACSignal ,所以它可以作为信号源被订阅者订阅,同时,它又实现了 RACSubscriber 协议,所以它也可以作为订阅者订阅其他信号源,这个就是 RACSubject 为什么可以手动控制。

RACSubject 有三个用来实现不同功能的子类:

  • RACGroupedSignal :分组信号,用来实现 RACSignal 的分组功能;
  • RACBehaviorSubject :重演最后值的信号,当被订阅时,会向订阅者发送它最后接收到的值;
  • RACReplaySubject :重演信号,保存发送过的值,当被订阅时,会向订阅者重新发送这些值。
RACSequence

RACSequence 代表的是一个不可变的值的序列。
一个 RACSequence 由两部分组成:

  • head :指的是序列中的第一个对象,如果序列为空,则为 nil ;
  • tail :指的是序列中除第一个对象外的其它所有对象,同样的,如果序列为空,则为 nil
NSArray *strings = @[@"1", @"22", @"333"];
RACSequence *results = [[strings.rac_sequence
    filter:^ BOOL (NSString *str) {
        return str.length >= 2;
    }] map:^(NSString *str) {
        return str;
    }];
    NSLog(@"--%@", results.array); //22  333

可以非常方便地使用 RACSequence 来实现集合的链式操作,直到得到你想要的最终结果为止。

RACSequence 的一系列功能也是通过类簇来实现的:

  • RACUnarySequence :一元序列,用来实现 RACSequence 的 +return: 方法;
  • RACIndexSetSequence :用来遍历索引集;
  • RACEmptySequence :空序列,用来实现 RACSequence 的 +empty 方法;
  • RACDynamicSequence :动态序列,使用 blocks 来动态地实现一个序列;
  • RACSignalSequence :用来遍历信号中的值;
  • RACArraySequence :用来遍历数组中的元素;
  • RACEagerSequence :非懒计算的序列,在初始化时立即计算所有的值;
  • RACStringSequence :用来遍历字符串中的字符;
  • RACTupleSequence :用来遍历元组中的元素。

2、RACSubscriber

在 RAC 中,订阅者是一个抽象的概念,所有实现了 RACSubscriber 协议的类都可以作为信号源的订阅者。


-sendNext: 、-sendError: 和 -sendCompleted 分别用来从 RACSignal 接收 next 、error 和 completed 事件,而 -didSubscribeWithDisposable: 则用来接收代表某次订阅的 disposable 对象。

订阅者对信号源的一次订阅过程可以抽象为:通过 RACSignal 的 -subscribe: 方法传入一个订阅者,并最终返回一个 RACDisposable 对象。

RACSubscriber 类的名字与 RACSubscriber 协议的名字相同,这跟 Objective-C 中的 NSObject 类的名字与 NSObject 协议的名字相同是一样的。通常来说,RACSubscriber 类充当的角色就是信号源的真正订阅者,实现了 RACSubscriber 协议。

3、RACScheduler

有了信号源和订阅者,我们还需要由调度器来统一调度订阅者订阅信号源的过程中所涉及到的任务,这样才能保证所有的任务都能够合理有序地执行。
RACScheduler 在 RAC 中就是扮演着调度器的角色,本质上,它就是用 GCD 的串行队列来实现的,并且支持取消操作。

RACScheduler 的一系列功能也是通过类簇来实现的:

  • RACImmediateScheduler :立即执行调度的任务,这是唯一一个支持同步执行的调度器;
  • RACQueueScheduler :一个抽象的队列调度器,在一个 GCD 串行列队中异步调度所有任务;
  • RACTargetQueueScheduler :继承自 RACQueueScheduler ,在一个以一个任意的 GCD 队列为 target 的串行队列中异步调度所有任务;
  • RACSubscriptionScheduler :一个只用来调度订阅的调度器。

4、RACDisposable

在订阅者订阅信号源的过程中,会产生副作用或者消耗一定的资源,所以当我们在取消订阅或者完成订阅时,我们就需要做一些资源回收和垃圾清理的工作。
RACDisposable 在 RAC 中就充当着清洁工的角色,它封装了取消和清理一次订阅所必需的工作。它有一个核心的方法 -dispose ,调用这个方法就会执行相应的清理工作。

RACDisposable 总共有四个子类:

  • RACSerialDisposable :作为 disposable 的容器使用,可以包含一个 disposable 对象,并且允许将这个 disposable 对象通过原子操作交换出来;
  • RACKVOTrampoline :代表一次 KVO 观察,并且可以用来停止观察;
  • RACCompoundDisposable :跟 RACSerialDisposable 一样,RACCompoundDisposable 也是作为 disposable 的容器使用。不同的是,它可以包含多个 disposable 对象,并且支持手动添加和移除 disposable 对象,有点类似于可变数组 NSMutableArray 。而当一个 RACCompoundDisposable 对象被 disposed 时,它会调用其所包含的所有 disposable 对象的 -dispose 方法,有点类似于 autoreleasepool 的作用;
  • RACScopedDisposable :当它被 dealloc 的时候调用本身的 -dispose 方法。

RAC 的使用

(以下代码例子来自:https://github.com/shuaiwang007/RAC

RACSignal
        // 1.创建信号
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            // 3.发送信号
            [subscriber sendNext:@"message"];
            // 4.取消信号,如果信号想要被取消,就必须返回一个RACDisposable
            // 信号什么时候被取消:1.自动取消,当一个信号的订阅者被销毁的时候机会自动取消订阅,2.手动取消,
            //block什么时候调用:一旦一个信号被取消订阅就会调用
            //block作用:当信号被取消时用于清空一些资源
            return [RACDisposable disposableWithBlock:^{
                NSLog(@"取消订阅");
            }];
        }];
        // 2. 订阅信号
        //subscribeNext, 把nextBlock保存到订阅者里面
        //只要订阅信号就会返回一个取消订阅信号的类
        RACDisposable *disposable = [signal subscribeNext:^(id  _Nullable x) {
            //只要信号内部发出数据就会调用这个block
            NSLog(@"x --- %@", x);
        }];
        // 取消订阅
        [disposable dispose];
RACSubject
        //创建信号
        RACSubject *subject = [RACSubject subject];
        //订阅信号
        [subject subscribeNext:^(id  _Nullable x) {
            // block:处理数据
            NSLog(@"%@",x);
        }];
        //发送信号
        [subject sendNext:@1];
        

RACSubject必须要先订阅信号之后才能发送信号, 而RACReplaySubject可以先发送信号后订阅。

RACMulticastConnection
        RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            NSLog(@"发送请求");
            // 发送信号
            [subscriber sendNext:@"message"];
            return nil;
        }];
        
        
        //每订阅一次信号就得重新创建并发送请求
        [signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"1--- %@", x);
        }];
        [signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"2--- %@", x);
        }];
        //发送请求
        // 1--- message
        // 发送请求
        // 2--- message
        
        
        //使用RACMulticastConnection,用一个信号内包装,不管有多少个订阅者,只发一次请求
        //创建连接类
        RACMulticastConnection *connection = [signal publish];
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"1--- %@", x);
        }];
        [connection.signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"2--- %@", x);
        }];
        //连接。只有连接了才会把信号源变为热信号
        [connection connect];
        //发送请求
        // 1--- message
        // 2--- message

绑定信号

        // 1.创建信号
        RACSubject *subject = [RACSubject subject];
        // 2.绑定信号
        RACSignal *bindSignal = [subject bind:^RACSignalBindBlock _Nonnull{
            
            //只要绑定信号订阅就会调用 block
            NSLog(@"0--- ");
            
            return ^RACSignal *(id value, BOOL *stop){
                // 只要源信号发送数据,就会调用block
                // value:源信号发送的内容 //value = @1; //可在这里修改发送信号的值
                NSLog(@"源信号的内容--- %@", value);
                //返回信号,不能为nil,如果非要返回空,则empty或 alloc init
                //把返回的值包装成信号
                return [RACSignal return:value];
            };
        }];
        
        // 3.订阅绑定信号
        [bindSignal subscribeNext:^(id x) {
            NSLog(@"绑定信号处理完的信号内容--- %@", x);
        }];
        // 4.发送信号
        [subject sendNext:@"message"];
        //0---
        //源信号的内容--- message
        //绑定信号处理完的信号内容--- message

过滤信号

        // 跳跃 : 如下,skip传入2 跳过前面两个值
        RACSubject *subject = [RACSubject subject];
        [[subject skip:2] subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];
        [subject sendNext:@1];
        [subject sendNext:@2];
        [subject sendNext:@3];
        //--- 3


        //distinctUntilChanged: 如果当前的值跟上一次的值一样,就不会被订阅到
        [[subject distinctUntilChanged] subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];


        // take:可以屏蔽一些值,去掉前面几个值---这里take为2 则只拿到前两个值
        [[subject take:2] subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];
        //takeLast:和take的用法一样,不过他取的是最后的几个值,如下,则取的是最后两个值
        [[subject takeLast:2] subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];
        [subject sendNext:@1];
        [subject sendNext:@2];
        [subject sendNext:@3];
        [subject sendCompleted];
        //--- 2
        //--- 3


        // takeUntil:---给takeUntil传的是哪个信号,
        //那么当这个信号发送信号或sendCompleted,就不能再接受源信号的内容了。
        [[subject takeUntil:subject2] subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];


        //ignore:忽略一些值. ignoreValues:表示忽略所有的值
        RACSignal *ignoreSignal = [subject ignore:@2]; //忽略值 @2
        [ignoreSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];


        // 一般和文本框一起用,添加过滤条件
        // 只有当文本框的内容长度大于5,才获取文本框里的内容
        [[self.textField.rac_textSignal filter:^BOOL(id value) {
            // value 源信号的内容
            return [value length] > 5;
            // 返回值 就是过滤条件。只有满足这个条件才能获取到内容
        }] subscribeNext:^(id x) {
            NSLog(@"%@", x);
        }];

映射
想要拦截服务器返回的数据,给数据拼接特定的东西或想对数据进行操作从而更改返回值,类似于这样的情况下,可以考虑用RAC的映射

        // 创建信号
        RACSubject *subject = [RACSubject subject];
        // 绑定信号
        RACSignal *bindSignal = [subject map:^id _Nullable(id  _Nullable value) {
            // 返回的类型就是你需要映射的值.
            return [NSString stringWithFormat:@"--- %@", value];
        }];
        // 订阅绑定信号
        [bindSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@", x);
        }];
        // 发送信号
        [subject sendNext:@"message"];
        //--- message


        // flattenMap中返回的是什么信号,订阅的就是什么信号
        RACSignal *bindSignal = [subject flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
            // value: 就是源信号发送的内容. 返回信号用来包装成修改内容的值
            return [RACSignal return:value];
        }];


        // flattenMap 主要用于信号中的信号
        // 创建信号
        RACSubject *signalofSignals = [RACSubject subject];
        RACSubject *signal = [RACSubject subject];
        // 订阅信号
        [[signalofSignals flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
            return value;
        }] subscribeNext:^(id  _Nullable x) {
            NSLog(@"%@", x);
        }];
        // 发送信号
        [signalofSignals sendNext:signal];
        [signal sendNext:@"123"];

组合信号
把多个信号聚合成想要的信号

        RACSignal *combinSignal = [RACSignal combineLatest:@[self.accountField.rac_textSignal, self.pwdField.rac_textSignal] reduce:^id(NSString *account, NSString *pwd){ //reduce里的参数一定要和combineLatest数组里的一一对应。
            // block: 只要源信号发送内容,就会调用,组合成一个新值。
            NSLog(@"%@ %@", account, pwd);
            return @(account.length && pwd.length);
        }];

        // 订阅信号
        [combinSignal subscribeNext:^(id x) {
            self.loginBtn.enabled = [x boolValue];
        }];
        // 可以直接用RAC宏
        //RAC(self.loginBtn, enabled) = combinSignal;



        //zipWith:把两个信号压缩成一个信号, 只有当两个信号同时发出信号内容时, 才会触发压缩流的next事件。
        RACSubject *signalA = [RACSubject subject];
        RACSubject *signalB = [RACSubject subject];
        // 压缩成一个信号
        RACSignal *zipSignal = [signalA zipWith:signalB];
        // 等所有信号都发送内容的时候才会调用
        [zipSignal subscribeNext:^(id x) {
            NSLog(@"--- %@", x); //所有的值都被包装成了元组
        }];
        //发送信号 元组内元素的顺序,跟发送的顺序无关,
        //而是跟压缩的顺序有关[signalA zipWith:signalB],先是A后是B
        [signalB sendNext:@2];
        [signalA sendNext:@1];
        //--- <RACTwoTuple> (1, 2)


        // merge:多个信号合并成一个信号,任何一个信号发送信息都会调用
        RACSubject *signalA = [RACSubject subject];
        RACSubject *signalB = [RACSubject subject];
        //组合信号
        RACSignal *mergeSignal = [signalA merge:signalB];
        // 订阅信号
        [mergeSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];
        //发送信号
        [signalA sendNext:@"message1"];
        [signalB sendNext:@"message2"];
        //--- message1
        //--- message2



        // then. 有两部分数据:想先获取上部分数据,然后进行下部分的,拿到下部分数据
        // 创建信号A
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            NSLog(@"A---");
            [subscriber sendNext:@"messageA"];
            [subscriber sendCompleted]; // 上部分信号发送完,必须要调用sendCompleted方法
            return nil;
        }];
        // 创建信号B,
        RACSignal *signalsB = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            NSLog(@"B---");
            [subscriber sendNext:@"messageB"];
            return nil;
        }];
        // 创建组合信号
        // then;忽略掉第一个信号的所有值
        RACSignal *thenSignal = [signalA then:^RACSignal * _Nonnull{
            // 返回的信号就是要组合的信号
            return signalsB;
        }];
        // 订阅信号
        [thenSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@", x);
        }];
        //A---
        //B---
        //--- messageB



        // concat. 有两部分数据:想让上部分先执行,完了之后再让下部分执行(都可获取值)
        // 创建信号A
        RACSignal *signalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
            NSLog(@"A---");
            [subscriber sendNext:@"messageA"];
            [subscriber sendCompleted]; // 上部分信号发送完必须要调用sendCompleted方法
            return nil;
        }];
        // 创建信号B
        RACSignal *signalsB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            NSLog(@"B---");
            [subscriber sendNext:@"messageB"];
            return nil;
        }];
        // concat:按顺序去链接
        // 创建组合信号
        RACSignal *concatSignal = [signalA concat:signalsB];
        // 订阅组合信号
        [concatSignal subscribeNext:^(id  _Nullable x) {
            NSLog(@"--- %@",x);
        }];
        //A---
        //--- messageA
        //B---
        //--- messageB


RACCommand

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

        // 1.创建命令
        RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            //执行命令的时候就会调用block. input传进来的参数
            NSLog(@"0--- %@", input);
            //返回值不允许为nil
            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                [subscriber sendNext:@"message"];
                return nil;
            }];
            
        }];
        
        // 订阅命令内部的信号. 直接订阅执行命令返回的信号
        // 2.执行命令
        RACSignal *signal =[command execute:@1];
        // 订阅信号
        [signal subscribeNext:^(id  _Nullable x) {
            NSLog(@"1--- %@", x);
        }];
        //0--- 1
        //1--- message
        // 方式二:
        // 订阅信号, 先订阅才能发送命令
        // command.executionSignals:信号源,信号中信号,signalofsignals:信号,发送数据就是信号
        // command.executionSignals.switchToLatest获取最新发送的信号,只能用于信号中信号。
        [command.executionSignals subscribeNext:^(id  _Nullable x) {
            [x subscribeNext:^(id  _Nullable x) {
                NSLog(@"2--- %@", x);
            }];
        }];

        //执行命令
        [command execute:@2];
        //0--- 2
        //2--- message

信号中的信号

        // switchToLatest--用于信号中信号
        // 创建信号中信号
        RACSubject *signalofsignals = [RACSubject subject];
        RACSubject *signalA = [RACSubject subject];
        
        [signalofsignals subscribeNext:^(RACSubject *x) {
            [x subscribeNext:^(id  _Nullable x) {
                NSLog(@"1--- %@", x);
            }];
        }];
        
        // switchToLatest: 获取信号中信号发送的最新信号
        [signalofsignals.switchToLatest subscribeNext:^(id  _Nullable x) {
            NSLog(@"2--- %@", x);
        }];
        
        // 发送信号
        [signalofsignals sendNext:signalA];
        [signalA sendNext:@4];

监听事件有没有完成

        // 监听事件有没有完成
        // 1.创建命令
        RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            //执行命令的时候就会调用block. input传进来的参数
            NSLog(@"0--- %@", input);
            //返回值不允许为nil
            return [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
                //发送数据
                [subscriber sendNext:@"message"];
                
                //发送完成, 当前命令内部发送数据完成,一定要主动发送完成
                [subscriber sendCompleted];
                return nil;
            }];
        }];
        
        // 监听事件有没有完成
        [command.executing subscribeNext:^(NSNumber * _Nullable x) {
            if ([x boolValue] == YES) {
                // 正在执行
                NSLog(@"1--- 正在执行 %@", x);
            }else {
                // 执行完成/没有执行
                NSLog(@"2--- 执行完成/没有执行");
            }
        }];
        
        // 2.执行命令
        [command execute:@1];
RACSequence

可以快速高效的遍历数组和字典:

        NSDictionary *dict = @{@"name":@"11", @"age":@"22"};
        
        [dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {
            RACTupleUnpack(NSString *key, NSString *value) = x;
            NSLog(@"--- %@ %@", key, value);
        } error:^(NSError * _Nullable error) {
            NSLog(@"--- %@", error);
        } completed:^{
            NSLog(@"--- completed");
        }];
        // --- name 11
        // --- age 22
        // --- completed
常用宏
        /**
         *  KVO
         *  RACObserveL:快速的监听某个对象的某个属性改变
         *  返回的是一个信号,对象的某个属性改变的信号
         */
        [RACObserve(self.view, center) subscribeNext:^(id x) {
            NSLog(@"%@", x);
        }];


        //例 textField输入的值赋值给label,监听label文字改变,
        RAC(self.label, text) = self.textField.rac_textSignal;
        [RACObserve(self.label, text) subscribeNext:^(id x) {
            NSLog(@"--- label的文字变了");
        }];


        /**
         *  循环引用问题
         *  使用 @weakify(self)和@strongify(self)来避免循环引用
         */
        @weakify(self)
        RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
            @strongify(self)
            NSLog(@"%@",self.view);
            return nil;
        }];


        /**
         * 快速包装一个元组
         * 把包装的类型放在宏的参数里面,就会自动包装
         */
        RACTuple *tuple = RACTuplePack(@1, @2, @4);
        // 宏的参数类型要和元祖中元素类型一致, 右边为要解析的元祖。
        RACTupleUnpack_(NSNumber *num1, NSNumber *num2, NSNumber * num3) = tuple;// 4.元祖
        // 快速包装一个元组
        // 把包装的类型放在宏的参数里面,就会自动包装
        NSLog(@"%@ %@ %@", num1, num2, num3);
        //1 2 4

有关 RAC 在 MVVM 架构中的使用,可以参考小例子:
GitHub: MV-X_Project

Reference

http://blog.leichunfeng.com/blog/2015/12/25/reactivecocoa-v2-dot-5-yuan-ma-jie-xi-zhi-jia-gou-zong-lan/
http://learnyouahaskell.com/chapters
http://williamzang.com/blog/2016/06/27/ios-kai-fa-xia-de-han-shu-xiang-ying-shi-bian-cheng/

相关文章

网友评论

      本文标题:RAC

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