ReactiveCocoa(FRP)-进阶篇(上)

简介: ReactiveCocoa(FRP)-进阶篇
  • 1.ReactiveCocoa常见操作方法介绍


  • 1.1 ReactiveCocoa操作须知
    所有的信号(RACSignal)都可以进行操作处理,因为所有操作方法都定义在RACStream.h中,而RACSignal继承RACStream。


  • 1.2. ReactiveCocoa操作思想


- 运用的是Hook(钩子)思想,Hook是一种用于改变API(应用程序编程接口:方法)执行结果的技术.
- Hook用处:截获API调用的技术。
- Hook原理:在每次调用一个API返回结果之前,先执行你自己的方法,改变结果的输出。
- RAC开发方式:RAC中核心开发方式,也是**绑定**,之前的开发方式是赋值,而用RAC开发,应该把重心放在绑定,也就是可以在创建一个对象的时候,就绑定好以后想要做的事情,而不是等赋值之后在去做事情。
- 列如:把数据展示到控件上,之前都是重写控件的setModel方法,用RAC就可以在一开始创建控件的时候,就绑定好数据。
  • 1.3 ReactiveCocoa核心方法bind


  • 1.3.1.ReactiveCocoa操作的核心方法是bind(绑定),给RAC中的信号进行绑定,只要信号一发送数据,就能监听到,从而把发送数据改成自己想要的数据。
  • 1.3.2.在开发中很少使用bind方法,bind属于RAC中的底层方法,RAC已经封装了很多好用的其他方法,底层都是调用bind,用法比bind简单.
  • 1.3.3.bind方法简单介绍和使用。


image.png

image.png

  • bind方法简单介绍和使用
    假设想监听文本框的内容,并且在每次输出结果的时候,都在文本框的内容拼接一段文字“输出:”
    方式一:在返回结果后,拼接。
    [_textField.rac_textSignal subscribeNext:^(id x) {


NSLog(@"输出:%@",x);
  }];
  • 方式二:在返回结果前,拼接,使用RAC中bind方法做处理。


bind方法参数:需要传入一个返回值是RACStreamBindBlock的block参数
 RACStreamBindBlock是一个block的类型,返回值是信号,参数(value,stop),因此参数的block返回值也是一个block。
RACStreamBindBlock:
参数一(value):表示接收到信号的原始值,还没做处理
参数二(*stop):用来控制绑定Block,如果*stop = yes,那么就会结束绑定。
返回值:信号,做好处理,在通过这个信号返回出去,一般使用RACReturnSignal,需要手动导入头文件RACReturnSignal.h。
  • bind方法使用步骤:
  • 1.传入一个返回值RACStreamBindBlock的block。
  • 2.描述一个RACStreamBindBlock类型的bindBlock作为block的返回值。
  • 3.描述一个返回结果的信号,作为bindBlock的返回值。
    注意:在bindBlock中做信号结果的处理。
  • 底层实现:
  • 1.源信号调用bind,会重新创建一个绑定信号。
  • 2.当绑定信号被订阅,就会调用绑定信号中的didSubscribe,生成一个bindingBlock。
  • 3.当源信号有内容发出,就会把内容传递到bindingBlock处理,调用bindingBlock(value,stop)
  • 4.调用bindingBlock(value,stop),会返回一个内容处理完成的信号(RACReturnSignal)。
  • 5.订阅RACReturnSignal,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。
  • 注意:不同订阅者,保存不同的nextBlock,看源码的时候,一定要看清楚订阅者是哪个。


这里需要手动导入#import <ReactiveCocoa/RACReturnSignal.h>,才能使用RACReturnSignal

[[_textField.rac_textSignal bind:^RACStreamBindBlock{
// 什么时候调用:
// block作用:表示绑定了一个信号.
return ^RACStream *(id value, BOOL *stop){
    // 什么时候调用block:当信号有新的值发出,就会来到这个block。
    // block作用:做返回值的处理
    // 做好处理,通过信号返回出去.
    return [RACReturnSignal return:[NSString stringWithFormat:@"输出:%@",value]];
};
}] subscribeNext:^(id x) {
      NSLog(@"%@",x);
}];
  • 1.4ReactiveCocoa操作方法之映射(flattenMap,Map)flattenMap,Map用于把源信号内容映射成新的内容


  • 1.4.1. flattenMap 用于信号中的信号,把源信号的内容映射成一个新的信号,信号可以是任意类型
    flattenMap使用步骤:
    1.传入一个block,block类型是返回值RACStream,参数value
    2.参数value就是源信号的内容,拿到源信号的内容做处理
    3.包装成RACReturnSignal信号,返回出去。
    flattenMap底层实现:
    0.flattenMap内部调用bind方法实现的,flattenMap中block的返回值,会作为bind中bindBlock的返回值。
    1.当订阅绑定信号,就会生成bindBlock。
    2.当源信号发送内容,就会调用bindBlock(value, *stop)
    3.调用bindBlock,内部就会调用flattenMap的block,flattenMap的block作用:就是把处理好的数据包装成信号。
    4.返回的信号最终会作为bindBlock中的返回信号,当做bindBlock的返回信号。
    5.订阅bindBlock的返回信号,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。


代码:

//1.创建信号
  RACSubject *subject =[RACSubject subject];
//2.绑定信号
  [[subject flattenMap:^RACStream *(id value) {
// block 只要原信号发送内容就会调用
// value 就是源信号发送的内容
   NSLog(@"11---%@",[NSThread currentThread]);
   value = [NSString stringWithFormat:@"JK %@",value];
 // 返回信号用来包装成修改内容的值
     return [RACReturnSignal return:value];
 }] subscribeNext:^(id x) {
 //3.订阅信号,flattenMap中返回的是什么信号,订阅的就是什么信号
   NSLog(@"%@",x);
    NSLog(@"22---%@",[NSThread currentThread]);
 }];
 //4.源信号发送信号
 [subject sendNext:@"123"];


image.png

1.4.2.map简单使用


Map作用:把源信号的值映射成一个新的值


map使用步骤:


  • 1.传入一个block,类型是返回对象,参数是value
  • 2.value就是源信号的内容,直接拿到源信号的内容做处理
  • 3.把处理好的内容,直接返回就好了,不用包装成信号,返回的值,就是映射的值。

Map底层实现:

  • 0.Map底层其实是调用flatternMap,Map中block中的返回的值会作为flatternMap中block中的值。
  • 1.当订阅绑定信号,就会生成bindBlock。
  • 2.当源信号发送内容,就会调用bindBlock(value, *stop)
  • 3.调用bindBlock,内部就会调用flattenMap的block
  • 4.flattenMap的block内部会调用Map中的block,把Map中的block返回的内容包装成返回的信号。
  • 5.返回的信号最终会作为bindBlock中的返回信号,当做bindBlock的返回信号。
  • 6.订阅bindBlock的返回信号,就会拿到绑定信号的订阅者,把处理完成的信号内容发送出来。


代码

//1.创建信号
    RACSubject *subject =[RACSubject subject];
//2.绑定信号
    [[subject map:^id(id value) {
// 返回的内容就是你要映射的值
   value = [NSString stringWithFormat:@"JK:%@",value];
   return value;
   }]subscribeNext:^(id x) {
      NSLog(@"映射之后的值= %@",x);
   }];
 //4.源信号发送信号
 [subject sendNext:@"123"];


image.png


  • 1.4.3.FlatternMap和Map的区别
  • 1.FlatternMap中的Block返回信号。
  • 2.Map中的Block返回对象。
  • 3.开发中,如果信号发出的值不是信号,映射一般使用Map
  • 4.开发中,如果信号发出的值是信号,映射一般使用FlatternMap。


  • 1.4.3.总结:signalOfsignals(信号中的信号)用FlatternMap**


// 创建信号中的信号
 RACSubject *signalOfsignals = [RACSubject subject];
 RACSubject *signal = [RACSubject subject];
 // 创建订阅者
 [[signalOfsignals flattenMap:^RACStream *(id value) {
     // 当signalOfsignals的signals发出信号才会调用
    return value;
 }] subscribeNext:^(id x) {
  // 只有signalOfsignals的signal发出信号才会调用,因为内部订阅了bindBlock中返回的信号,也就是flattenMap返回的信号。
  // 也就是flattenMap返回的信号发出内容,才会调用。
   NSLog(@"%@aaa",x);
}];
// 信号的信号发送信号
 [signalOfsignals sendNext:signal];
// 信号发送内容
[signal sendNext:@1];


image.png

1.4.4.信号中的信号创建订阅者的3种方式

image.png



// 1.创建信号中的信号
RACSubject *signalOfsignals = [RACSubject subject];
RACSubject *signal = [RACSubject subject];
//2.创建订阅者的种方式
   //第1种方式
[signalOfsignals.switchToLatest subscribeNext:^(id x) {
    NSLog(@"%@",x);
}];
// 第2种方式
[signalOfsignals subscribeNext:^(RACSignal *x) {
    [x subscribeNext:^(id x) {
        NSLog(@"%@",x);
    }];
}];
 // 第3种方式
[[signalOfsignals flattenMap:^RACStream *(id value) {
    // 当signalOfsignals的signals发出信号才会调用
    return value;
}] subscribeNext:^(id x) {
    // 只有signalOfsignals的signal发出信号才会调用,因为内部订阅了bindBlock中返回的信号,也就是flattenMap返回的信号。
    // 也就是flattenMap返回的信号发出内容,才会调用。
    //5.打印信号发送的内容(可以进行修改)
    NSLog(@"%@aaa",x);
}];
// 3.信号的信号发送信号
[signalOfsignals sendNext:signal];
// 4.信号发送内容
[signal sendNext:@1];
  • 1.5 ReactiveCocoa操作方法之组合


  • 1.5.1.concat:按一定顺序拼接信号,当多个信号发出的时候,有顺序的接收信号。


RACSignal *signalA = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendCompleted];
   return nil;
}];
RACSignal *signalB = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
   [subscriber sendNext:@2];
   return nil;
}];
// 把signalA拼接到signalB后,signalA发送完成,signalB才会被激活。
RACSignal *concatSignal = [signalA concat:signalB];
// 以后只需要面对拼接信号开发。
// 订阅拼接的信号,不需要单独订阅signalA,signalB
// 内部会自动订阅。
// 注意:第一个信号必须发送完成,第二个信号才会被激活
[concatSignal subscribeNext:^(id x) {
     NSLog(@"%@",x);
}];
  • 注意:第一个信号必须发送完成[subscriber sendCompleted];,第二个信号才会被激活,按顺序打印出来.(前后按顺序实现,打印)


concat底层实现:


-  1.当拼接信号被订阅,就会调用拼接信号的didSubscribe
  -  2.didSubscribe中,会先订阅第一个源信号(signalA)
  -  3.会执行第一个源信号(signalA)的didSubscribe
  -  4.第一个源信号(signalA)didSubscribe中发送值,就会调用第一个源信号(signalA)订阅者的nextBlock,通过拼接信号的订阅者把值发送出来.
  -  5.第一个源信号(signalA)didSubscribe中发送完成,就会调用第一个源信号(signalA)订阅者的completedBlock,订阅第二个源信号(signalB)这时候才激活(signalB)。
  -  6.订阅第二个源信号(signalB),执行第二个源信号(signalB)的didSubscribe
  -  7.第二个源信号(signalA)didSubscribe中发送值,就会通过拼接信号的订阅者把值发送出来.
  • 1.5.2.then:用于连接两个信号,当第一个信号完成,才会连接then返回的信号
    then:用于连接两个信号,当第一个信号完成,才会连接then返回的信号
    注意使用then,之前信号的值会被忽略掉.(忽略掉前面的,只打印后面的)
    底层实现:


1、先过滤掉之前的信号发出的值。
2、使用concat连接then返回的信号。


then代码


[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {

[subscriber sendNext:@1];
    [subscriber sendCompleted];
   return nil;
 }] then:^RACSignal *{
     return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
  [subscriber sendNext:@2];
  return nil;
   }];
 }] subscribeNext:^(id x) {
   // 只能接收到第二个信号的值,也就是then返回信号的值
   NSLog(@"%@",x);
}];


目录
相关文章
|
2月前
|
PHP Windows
thinkPhP6.0安装教程图解--PHP框架安装
本文是一篇关于ThinkPHP 6.0安装教程的图解,包括环境检查、安装Composer、修改Composer镜像地址、安装ThinkPHP框架以及启动运行ThinkPHP的步骤。文章详细描述了每个步骤的操作方法,并提供了相应的命令和截图,帮助用户理解并顺利完成ThinkPHP 6.0的安装和运行。
thinkPhP6.0安装教程图解--PHP框架安装
|
3月前
|
开发工具 图形学 Android开发
从零开始的unity3d入门教程(一)----环境配置
该文章是《从零开始的Unity3D入门教程》系列的第一篇,详细介绍了Unity3D的环境配置过程,包括注册Unity账户、下载安装Unity Hub和Unity编辑器、配置许可证、创建Unity项目、下载安装Visual Studio 2022以及将Unity与Visual Studio相关联等步骤。
从零开始的unity3d入门教程(一)----环境配置
|
3月前
|
定位技术 C# 图形学
从零开始的unity3d入门教程(二)----基本功能讲解
这是一篇Unity3D入门教程,详细介绍了Unity界面操作、游戏物体创建修改、场景搭建、玩家控制、音效添加以及游戏测试和导出的全过程。
从零开始的unity3d入门教程(二)----基本功能讲解
|
机器学习/深度学习 存储 JavaScript
你就是函数响应式编程(FRP)啊?!【附 RxJS 实战】
什么是 FRP? 英文全称是:Functional Reactive Programming,翻译过来就是:函数响应式编程。
|
安全 Java Linux
手把手教你搭个Frida + Sekiro Rpc框架
手把手教你搭个Frida + Sekiro Rpc框架
手把手教你搭个Frida + Sekiro Rpc框架
ReactiveCocoa(FRP)-进阶篇(下)
ReactiveCocoa(FRP)-进阶篇(下)
150 0
ReactiveCocoa(FRP)-进阶篇(下)
|
存储 前端开发 JavaScript
werkzeug源码阅读-完结篇
Werkzeug是一个全面的WSGI Web应用程序库。它最初是WSGI实用程序各种工具的简单集合,现已成为最高级的WSGI实用程序库之一,是Flask背后的项目。
503 0
werkzeug源码阅读-完结篇
|
应用服务中间件 nginx
Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》(1)
Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》
197 0
Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》(1)
|
容器
Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》(2)
Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》
173 0
Phalcon如何创建多模块并能进行访问 《Phalcon入坑指南系列 四》(2)
|
Web App开发 监控 Shell
实现原理讲解,我强烈推荐你看看这-22-款超好用的命令行工具
实现原理讲解,我强烈推荐你看看这-22-款超好用的命令行工具
实现原理讲解,我强烈推荐你看看这-22-款超好用的命令行工具
下一篇
无影云桌面