ReactiveCocoa入门第一课

简介
ReactiveCocoa(RAC):RAC是一套基于Cocoa的FRP(Functional Reactive Programming:函数响应式编程)框架。
安装:可通过CocoaPods: pod "ReactiveObjC"
思想
学习一种新的编程范式,困难不在于一门编程语言,而是怎么学会用另一种方式去思考。思路比语法更重要。
FRP(Functional Reactive Programming)函数响应式编程,这是RAC的核心思想。FRP是一种编程范式,是组合了函数式编程和响应式编程范式。接下来熟悉一下这两个编程范式。
函数式编程
如何理解?
如果说面向对象是对数据的抽象,那么函数式编程就是对行为的抽象。
就是允许把函数本身作为参数传入另一个函数,还允许返回一个函数!它的核心是函数。在解决问题时,使用不可变值和函数,函数对一个值进行处理,映射成另一个值。
关键点: 参数是个函数 返回值也是函数
现实生活中行为:
洗车:可以将函数比作一个自动洗车间,脏的汽车进去,自动洗完后,出来还是汽车,只是汽车变干净了。
响应式编程
如何理解?
面向数据流的编程思想,类似观察者模式一样的表现。
计算是前后呼应的,相互关联,有些变化了,彼此之间的关系也会随着值的变化而变化。
现实生活中行为:
防火:室内烟雾散发到一定程度时,烟感探测器探测到安全极限,及时发出失火警报。
关键点: 相互关联、跟随变化
开始学习RAC
RAC中Signal的基本的订阅过程

创建信号

订阅信号

发送信号

取消信号

RAC常用的类
RACSignal
RACSignal(冷信号)继承于抽象类RACStream,是RAC的核心类,信号只有被订阅时才会送出信号值。
冷信号特点:

不可变;
被动;

创建冷信号
在RACSignal.h中的声明的createSignal类方法如下:
c复制代码+ (RACSignal<ValueType> *)createSignal:(RACDisposable * _Nullable (^)(id<RACSubscriber> subscriber))didSubscribe RAC_WARN_UNUSED_RESULT;

createSignal 类方法说明:

作用:创建新的信号
参数:didSubscribe,是一个block类型变量,didSubscribe block变量自身是一个类似C语言的函数指针变量,didSubscribe 指向的函数包含一个RACSubscriber 参数,返回一个RACDisposable 对象。
返回值:返回一个RACSignal类型的信号。

例:

//创建信号
  RACSignal *singalA = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber>  _Nonnull subscriber) {
    //发送信号
        [subscriber sendNext:@"1"];
        [subscriber sendNext:@(2)];
        [subscriber sendCompleted];
    
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"------this is RACDisposable---------");
        }];
    }];
    //订阅信号
    [singalA subscribeNext:^(id  _Nullable x) {
        NSLog(@"-------singalA value is %@-------",x);
    }];
返回结果如下:
2021-04-10 22:10:30.127793+0800 RACDemo[14148:238689] -------singalA value is 1-------
2021-04-10 22:10:30.127938+0800 RACDemo[14148:238689] -------singalA value is 2-------
2021-04-10 22:10:30.128098+0800 RACDemo[14148:238689] ------this is RACDisposable---------

上面的过程拆分一下可以这样写:

 //声明一名称为didSubscribe的block变量,带有一个RACSubscriber类型参数,返回一个RACDisposable类型对象
    RACDisposable * (^didSubscribe)(id<RACSubscriber> subscriber);
    //为didSubscribe定义,(block的实现)
    didSubscribe = ^RACDisposable * (id<RACSubscriber> sub){
      //发送信号
        [sub sendNext:@"1"];
        [sub sendNext:@(2)];
        [sub sendCompleted];
        [sub sendError:nil];
        return [RACDisposable disposableWithBlock:^{
            NSLog(@"----didSubscribe--RACDisposable--------");
        }];
    };
    //创建信号
    RACSignal *signalB = [RACSignal createSignal:didSubscribe];
    //订阅信号
    [signalB subscribeNext:^(id  _Nullable x) {
        NSLog(@"------signalB x is %@--------",x);
    }];
返回结果如下:
2021-04-10 22:13:58.758873+0800 RACDemo[14258:241973] ------signalB x is 1--------
2021-04-10 22:13:58.758993+0800 RACDemo[14258:241973] ------signalB x is 2--------
2021-04-10 22:13:58.759078+0800 RACDemo[14258:241973] ----didSubscribe--RACDisposable-------

RACSubscriber
订阅者:负责发信号,本身是个协议;只要遵循它,并且实现协议内的方法,就会成为订阅者。
常用的几个方法:

//发送值到订阅者
- (void)sendNext:(nullable id)value;
//发送错误给订阅者
- (void)sendError:(nullable NSError *)error;
//告诉订阅者发送完成了
- (void)sendCompleted;

RACDisposable
取消订阅或清理资源,发送失败或者完成,会自动触发取消订阅方法。

+ (instancetype)disposableWithBlock:(void (^)(void))block;
//取消订阅
- (void)dispose;

RACSubject
RACSubject(热信号)t继承于RACSignal,一个subject可认为是一个信号,你可以手动控制sendNext、sendError、sendCompleted事件。可以帮助你将非RAC桥接到RAC上。
热信号特点:

可变;

主动;

可以桥接RAC和非RAC;

RACSubject 对外公开的接口只有两个,一个是创建一个热信号对象,另一个是发送消息的方法。

/// Returns a new subject.
+ (instancetype)subject;

// Redeclaration of the RACSubscriber method. Made in order to specify a generic type.
- (void)sendNext:(nullable ValueType)value;

创建热信号的接口:

  • (instancetype)subject;
    这个方法创建的时候,会初始化两个私有属性,一个是subscribers数组 、另一个是disposable。
    subscribers:数组里包含了所有的当前订阅者,在self的同步操作时候会使用它遍历所属的订阅者。
    disposable:包含接收者对其他信号的所有订阅。主要是用于取消订阅和清理资源的操作。
    发送信号的接口:
  • (void)sendNext:(nullable ValueType)value;
    重新定义了RACSubscriber协议的sendNext方法。
    创建热信号
    例:创建一个热信号
  //创建信号
    RACSubject *subject=[RACSubject subject];
    //订阅信号
    [subject subscribeNext:^(id  _Nullable x) {
        NSLog(@"subject x is [%@]",x);
    }];
    //发送信号
    [subject sendNext:@"123"];
    //结束订阅
    [subject sendCompleted];
返回结果如下:
  2021-04-13 16:48:52.043076+0800 RACDemo[29017:264320] subject x is [123]

常用高阶函数
绑定 bind
作用: 可将信号拦截后进行过滤再发送给新的信号,类似消息转发

typedef RACSignal * _Nullable (^RACSignalBindBlock)(ValueType _Nullable value, BOOL *stop);
- (RACSignal *)bind:(RACSignalBindBlock (^)(void))block RAC_WARN_UNUSED_RESULT;

例:

    //创建信号
     RACSubject *subject = [RACSubject subject];
    //绑定新的信号
    RACSignal *singnal=[subject bind:^RACSignalBindBlock _Nonnull {
        return ^RACSignal*(id value,BOOL *stop){
            return[RACReturnSignal return:value];
        };
    }];
    // 订阅信号
    [singnal subscribeNext:^(id  _Nullable x) {
        NSLog(@"bind x is [%@]",x);
    }];
    //发送信号
    [subject sendNext:@"send bind value 123"];
返回结果如下:
2021-04-12 10:16:57.562421+0800 RACDemo[4075:59403] bind x is [send bind value 123]

合并
concat
作用:按顺序合并多个信号为一个信号,其中前一个信号必须发送sendCompleted表示执行完成,才会执行后面一个信号,类似串行队列,先完成一个请求,才进行下一个请求。

- (RACSignal *)concat:(RACSignal *)signal RAC_WARN_UNUSED_RESULT;

例:

   //创建信号
    RACSubject *subject=[RACSubject subject];
    //创建信号
    RACSubject *subject2=[RACSubject subject];
    //连接信号
    RACSignal *signal=[subject concat:subject2];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"concat x is [%@]",x);
    }];
    //发送信号
    [subject sendNext:@"abc"];
    //subject 信号执行完成,必须加上 sendCompleted,表示信号结束了,否则无法连接下一个信号
    [subject sendCompleted];
    //subject2发送信号
    [subject2 sendNext:@"ABC"];
返回结果如下:
2021-04-12 16:42:14.281303+0800 RACDemo[17888:262262] concat x is [abc]
2021-04-12 16:42:14.281532+0800 RACDemo[17888:262262] concat x is [ABC]

merge
作用:按顺序合并多个信号为一个信号,其中前一个信号不添加sendCompleted,也会执行后面一个信号。只要有一个信号来,合并操作就会接受。不强调先完成一个再进行下一个。
例:

 //创建信号
      RACSubject *subject=[RACSubject subject];
      //创建信号
      RACSubject *subject2=[RACSubject subject];
      //连接信号
      RACSignal *signal=[subject merge:subject2];
      //订阅信号
      [signal subscribeNext:^(id  _Nullable x) {
          NSLog(@"merge x is [%@]",x);
      }];
      //发送信号
      [subject sendNext:@"abc"];
      //subject2发送信号
      [subject2 sendNext:@"ABC"];
返回结果如下:
2021-04-12 17:08:23.410822+0800 RACDemo[19726:287122] merge x is [abc]
2021-04-12 17:08:23.411005+0800 RACDemo[19726:287122] merge x is [ABC]

combineLatest
作用:按顺序合并多个信号的最新值为元组
例:

//创建信号
    RACSubject *subject = [RACSubject subject];
    //创建信号
    RACSubject *subject2 = [RACSubject subject];
    RACSignal *signal = [RACSignal combineLatest: @[subject, subject2]];
    //订阅信号
       [signal subscribeNext:^(id  _Nullable x) {
           NSLog(@"combineLatest x is  [%@]",x);
       }];
    //发送信号
    [subject sendNext:@"a"];
    //发送信号
    [subject2 sendNext:@"b"];
    //发送信号
    [subject2 sendNext:@"A"];
   //发送信号
    [subject sendNext:@"B"];
返回结果如下:
  2021-04-12 17:27:35.030304+0800 RACDemo[20891:303336] combineLatest x is  [<RACTuple: 0x600003af40b0> (
    a,
    b
)]
2021-04-12 17:27:35.030604+0800 RACDemo[20891:303336] combineLatest x is  [<RACTuple: 0x600003ae4ee0> (
    a,
    A
)]
2021-04-12 17:27:35.030805+0800 RACDemo[20891:303336] combineLatest x is  [<RACTuple: 0x600003ae85c0> (
    B,
    A
)]

映射
flattenMap
作用: 用于信号中信号,把源信号的内容映射成一个新的信号,信号是RACSignal类型

- (RACSignal *)flattenMap:(__kindof RACSignal * _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;

例:映射值后,返回一下新的信号

    //创建信号
    RACSubject *subject = [RACSubject subject];
    // 绑定信号,并返回映射后的值
    RACSignal * flattenMapSignal =[subject flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {
        value=[NSString stringWithFormat:@"flattenMapSignal映射后的值为:%@",value];
        return [RACReturnSignal return:value];
    }];
    //订阅信号
    [flattenMapSignal subscribeNext:^(id  _Nullable x) {
         NSLog(@"flattenMapSignal x is [%@]",x);
      }];
    //发送信号
    [subject sendNext:@"1234"];
返回结果如下:
2021-04-11 08:30:20.425283+0800 RACDemo[5943:81326] flattenMapSignal x is [flattenMapSignal映射后的值为:1234]

map
作用: 把源信号的值映射成一个新的值,可以是任意类型的

- (RACSignal *)map:(id _Nullable (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;

例:返回加工后的字符串

   //创建信号
        RACSubject *subject = [RACSubject subject];
        //绑定信号
    RACSignal *bindSignal = [subject map:^id _Nullable(id  _Nullable value) {
        //信号发送的内容
        NSString *str = [NSString stringWithFormat:@"映射 map 处理后的数据:%@", value];
        //返回任意类型的值
            return str;
        }];
        //订阅信号
        [bindSignal subscribeNext:^(id x) {
        NSLog(@"map x is [%@]",x);
        }];
        //发送数据
        [subject sendNext:@1];
返回结果如下:
  2021-04-11 08:34:34.676217+0800 RACDemo[6105:85742] map x is [映射 map 处理后的数据:1]

例:返回一个新的信号

 RACSubject *subject2=[RACSubject subject];
    //绑定信号
   RACSignal * bindSignal2=[subject2 map:^RACSignal * _Nullable(id  _Nullable value) {
       //映射后,返回一个新的信号对象
       value=[NSString stringWithFormat:@"修改value %@",value];
      return [RACReturnSignal return:value];
    }];
    //订阅信号
    [bindSignal2 subscribeNext:^(id  _Nullable x) {
        NSLog(@"返回值是信号  [%@]",x);
    }];
    //发送信号
    [subject2 sendNext:@(2)];
返回结果如下:
2021-04-11 08:41:03.927420+0800 RACDemo[6274:90310] 返回值是信号  [<RACReturnSignal: 0x600001e9d400> name: ]
    

过滤
filter
作用: 通过条件判断,过滤掉相应的信号

- (RACSignal<ValueType> *)filter:(BOOL (^)(ValueType _Nullable value))block RAC_WARN_UNUSED_RESULT;

例:过滤掉ABC开头的字符串

 //创建信号
    RACSubject *subject=[RACSubject subject];
    //过滤掉ABC开头的字符串
    RACSignal *signal= [subject filter:^BOOL(id  _Nullable value) {
        NSString *str=value;
        if ([str hasPrefix:@"ABC"]) {
            return NO;
        }else{
            return YES;
        }
    }];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"过滤后的结果为 [%@]",x);
    }];
    //发送信号
    [subject sendNext:@"abc123"];
    //发送信号
    [subject sendNext:@"ABC123"];
返回结果如下:
2021-04-11 10:44:03.423107+0800 RACDemo[10384:185744] 过滤后的结果为 [abc123]

ignore
作用: 过滤信号值,忽略掉值为xxx的信号,按值忽略

- (RACSignal<ValueType> *)ignore:(nullable ValueType)value RAC_WARN_UNUSED_RESULT;

例:忽略掉值为ABC123的信号

 //创建信号
    RACSubject* subject=[RACSubject subject];
    //忽略掉值为ABC123的信号
    RACSignal *signal=[subject ignore:@"ABC123"];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"ignore x is [%@]",x);
    }];
    //发送信号
    [subject sendNext:@"ABC123"];
    //发送信号
    [subject sendNext:@"abc123"];
返回结果如下:
2021-04-11 10:37:35.490486+0800 RACDemo[10205:180228] ignore x is [abc123]

then

- (RACSignal *)then:(RACSignal * (^)(void))block RAC_WARN_UNUSED_RESULT;

作用:忽略掉前一个信号发送的值,必须等前一个信号发送sendCompleted表示执行完成后,才执行下一个信号,否则无效,按信号忽略
例:忽略掉subject 信号,然后再执行subject2信号

    //创建信号
    RACSubject *subject = [RACSubject subject];
    //创建信号
    RACSubject *subject2 = [RACSubject subject];
    //忽略掉subject 信号
    RACSignal *signal = [subject then:^RACSignal * _Nonnull{
        return subject2;
    }];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"then x is  [%@]",x);
    }];
    
    //发送信号
    [subject sendNext:@"a"];
    // 必须添加sendCompleted ,表示subject 信号执行完成,否则无效
    [subject sendCompleted];
    //发送信号
    [subject2 sendNext:@"b"];
    //发送信号
    [subject2 sendNext:@"A"];
返回结果如下:
2021-04-12 17:45:59.354229+0800 RACDemo[22106:321645] then x is  [b]
2021-04-12 17:45:59.354343+0800 RACDemo[22106:321645] then x is  [A]

take
作用: 按顺序获取第1次到n次信号

- (RACSignal<ValueType> *)take:(NSUInteger)count RAC_WARN_UNUSED_RESULT;

例:获取从第1~4次信号

    //创建信号
    RACSubject* subject=[RACSubject subject];
    //获取从第1~4次信号,若传0则不取信号
    RACSignal *signal=[subject take:4];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"take x is [%@]",x);
    }];
    //发送信号
    [subject sendNext:@"ABC123"];
    //发送信号
    [subject sendNext:@"123ABC"];
    //发送信号
    [subject sendNext:@"abc123"];
    //发送信号
    [subject sendNext:@"123abc"];
    //发送信号
    [subject sendNext:@"123"];
    //发送信号
    [subject sendNext:@"abc"];
返回结果如下:
2021-04-11 10:59:05.655107+0800 RACDemo[10795:197326] take x is [ABC123]
2021-04-11 10:59:05.655298+0800 RACDemo[10795:197326] take x is [123ABC]
2021-04-11 10:59:05.655436+0800 RACDemo[10795:197326] take x is [abc123]
2021-04-11 10:59:05.655573+0800 RACDemo[10795:197326] take x is [123abc]

skip
作用: 跳过从第1次~n次 的信号
例:跳过从第1~3次信号

 //创建信号
    RACSubject* subject=[RACSubject subject];
    //跳过从第1~3次信号
    RACSignal *signal=[subject skip:3];
    //订阅信号
    [signal subscribeNext:^(id  _Nullable x) {
        NSLog(@"skip x is [%@]",x);
    }];
    //发送信号
    [subject sendNext:@"123"];
    //发送信号
    [subject sendNext:@"abc"];
    //发送信号
    [subject sendNext:@"ABC"];
    //发送信号
    [subject sendNext:@"123abc"];
返回结果如下:
2021-04-11 11:18:20.866481+0800 RACDemo[11294:209783] skip x is [123abc]

switchToLatest
作用: 信号内的信号,获取信号内信号发送的最新的信号,当发送多个内部信号时候,只接受最新的内部信号
例:

    //创建信号
    RACSubject* subject=[RACSubject subject];
    //创建内部信号,用于subject内部的信号
    RACSubject * innerSubject=[RACSubject subject];
    //创建内部信号1,用于subject内部的信号
      RACSubject * innerSubject1=[RACSubject subject];
    //订阅信号,订阅subject内的即innerSubject信号
    [subject.switchToLatest subscribeNext:^(id  _Nullable x) {
        NSLog(@"switchToLatest x is [%@]",x);
    }];
    //subject发送innerSubject信号
    [subject sendNext:innerSubject];
    //subject发送innerSubject1信号
    [subject sendNext:innerSubject1];
    //通过innerSubject来发送信号
    [innerSubject sendNext:@"abc"];
   //通过innerSubject1来发送信号
    [innerSubject1 sendNext:@"123abc"];
返回结果如下:
2021-04-11 11:41:27.553097+0800 RACDemo[12001:229779] switchToLatest x is [123abc]

distinctUntilChanged
作用: 忽略重复的信号
例:执行后,忽略了重复的1

 //创建信号
    RACSubject* subject=[RACSubject subject];
    //订阅信号,只在订阅的信号与上一次不同时才会响应
    [subject.distinctUntilChanged subscribeNext:^(id  _Nullable x) {
        NSLog(@"distinctUntilChanged x is [%@]",x);
    }];
     //发送信号
    [subject sendNext:@"1"];
     //发送信号
    [subject sendNext:@"1"];
     //发送信号
     [subject sendNext:@"123"];
返回结果如下:
2021-04-11 11:53:14.833259+0800 RACDemo[12347:240090] distinctUntilChanged x is [1]
2021-04-11 11:53:14.833524+0800 RACDemo[12347:240090] distinctUntilChanged x is [123]

总结
RAC的高内聚,低耦合特性,很好的配合了MVVM的使用。因此如果现在正在使用MVVM模式开发,不妨试一下RAC框架。 以上是对RAC中的常用类、函数、宏的一些使用说明,并未对每个功能的底层原理进行分析,主要是介绍RAC是如何应用于业务中的。学习一项新的框架的步骤,一般是先知其所用,而后再知其所以然,后续再对某些功能进行详细的分析。

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

推荐阅读更多精彩内容