学习 RAC 我们首先要了解 RAC 都有哪些类

  • RACSignal
  • RACSubject
  • RACSequence
  • RACMulticastConnection
  • RACCommand

在学习的时候写了一个小 demo 来分别介绍每个类的作用,gitHub 地址: https://github.com/SummerHH/ReactiveCocoa.git

demo 的目录结构如下

RAC学习起来的特点

  • 学习起来比较难
  • 团队开发的时候需要谨慎使用
  • 团队代码需要不断的评审,保证团队中所有人代码的风格一致!避免阅读代码的困难

RAC 导入:

使用  pod 'ReactiveCocoa', '~> 2.5' 导入

这里版本说下2.5以下是 Object-C 不支持 swift

2.5 以上开始支持 swift

项目如果是 OC 写的话建议导入2.5这个版本

RACSignal:

RACSiganl:信号类,一般表示将来有数据传递,只要有数据改变,信号内部接收到数据,就会马上发出数据。

信号类(RACSiganl),只是表示当数据改变时,信号内部会发出数据,它本身不具备发送信号的能力,而是交给内部一个订阅者去发出。

默认一个信号都是冷信号,也就是值改变了,也不会触发,只有订阅了这个信号,这个信号才会变为热信号,值改变了才会触发。

如何订阅信号:调用信号RACSignal的subscribeNext就能订阅。

//1.创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { //3.发送信号
[subscriber sendNext:@"发送信号"];
/**
如果不在发送数据,最好发送信号完成,内部会自动调用[RACDisposable disposable]取消订阅信号
*/
[subscriber sendCompleted]; //取消订阅方法
return [RACDisposable disposableWithBlock:^{
//block调用时刻:当信号发送完成或者发送错误,就会自动执行这个block,取消订阅信号
// 执行完Block后,当前信号就不在被订阅了。
NSLog(@"信号销毁了");
}];
}]; //2.订阅信号
[signal subscribeNext:^(id x) {
NSLog(@"订阅信号:%@",x);
}];

   //输出

//订阅信号:发送信号

//信号销毁了

RACSignal底层实现

1.创建信号,首先把didSubscribe保存到信号中,还不会触发。

2.当信号被订阅,也就是调用signal的subscribeNext:nextBlock

2.1 subscribeNext内部会调用siganl的didSubscribe

2.2 subscribeNext内部会创建订阅者subscriber,并且把nextBlock保存到subscriber中

3.siganl的didSubscribe中调用[subscriber sendNext:@"发送信号"];

3.1 sendNext底层其实就是执行subscriber的nextBlock

注意:

当一个信号被全局变量保存值的时候,我们要手动取消订阅

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

        /**
有一个全局变量保存值就不会走下面取消订阅方法
*/
_subscriber = subscriber; [subscriber sendNext:@""];
[subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{
NSLog(@"走销毁这个方法了");
}];
}];

RACDisposable 取消订阅

    //订阅信号返回RACDisposable
RACDisposable *disposable = [signal1 subscribeNext:^(id x) {
NSLog(@"接收打印的值:%@",x);
}]; // 默认一个信号发送数据完毕们就会主动取消订阅.
// 只要订阅者在,就不会自动取消信号订阅
// 手动取消订阅者
[disposable dispose];

RACSubscriber:

表示订阅者的意思,用于发送信号,这是一个协议,不是一个类,只要遵守这个协议,并且实现方法才能成为订阅者。通过create创建的信号,都有一个订阅者,帮助他发送数据。

RACDisposable:用于取消订阅或者清理资源,当信号发送完成或者发送错误的时候,就会自动触发它。

使用场景:不想监听某个信号时,可以通过它主动取消订阅信号。

RACSubject:RACSubject:信号提供者,自己可以充当信号,又能发送信号。

使用场景:通常用来代替代理,有了它,就不必要定义代理了。

RACReplaySubject:重复提供信号类,RACSubject的子类。

RACReplaySubject与RACSubject区别:

RACReplaySubject可以先发送信号,在订阅信号,RACSubject就不可以。

使用场景一:如果一个信号每被订阅一次,就需要把之前的值重复发送一遍,使用重复提供信号类。

使用场景二:可以设置capacity数量来限制缓存的value的数量,即只缓充最新的几个值。

// RACSubject使用步骤

// 1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。

// 2.订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

// 3.发送信号 sendNext:(id)value

 //1.创建信号
RACSubject *subject = [RACSubject subject];
//2.订阅信号
[subject subscribeNext:^(id x) {
//当信号发出新值,就会调用.
NSLog(@"第一个订阅者%@",x);
}]; [subject subscribeNext:^(id x) {
NSLog(@"第二个订阅者%@",x);
}]; //3.发送信号
[subject sendNext:@""];
//4.发送信号完成,内部会自动取消订阅者
[subject sendCompleted]; //输出:
// 第一个订阅者123456
// 第二个订阅者123456

RACReplaySubject

RACReplaySubject使用步骤:

1.创建信号 [RACSubject subject],跟RACSiganl不一样,创建信号时没有block。

2.可以先订阅信号,也可以先发送信号。

2.1 订阅信号 - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock

2.2 发送信号 sendNext:(id)value

RACReplaySubject:底层实现和RACSubject不一样。

1.调用sendNext发送信号,把值保存起来,然后遍历刚刚保存的所有订阅者,一个一个调用订阅者的nextBlock。

2.调用subscribeNext订阅信号,遍历保存的所有值,一个一个调用订阅者的nextBlock

    //1.
RACReplaySubject *replaySubject = [RACReplaySubject subject];
//2.发送信号
[replaySubject sendNext:@""];
[replaySubject sendNext:@""];
//3.订阅信号
[replaySubject subscribeNext:^(id x) {
NSLog(@"第一个订阅者接收到的数据%@",x);
}]; [replaySubject subscribeNext:^(id x) { NSLog(@"第二个订阅者接收到的数据%@",x);
}]; //输出:
//第一个订阅者接收到的数据1
//第一个订阅者接收到的数据2
//第二个订阅者接收到的数据1
//第二个订阅者接收到的数据2

RAC集合


RACTuple:元组类,类似NSArray,用来包装值.

 /**
RACTuple:元组类,类似NSArray,用来包装值.
*/
//元组
RACTuple *tuple = [RACTuple tupleWithObjectsFromArray:@[@"",@"",@]];
NSString *first = tuple[];
NSLog(@"%@",first); //输出:
//

数组

   NSArray *arr = @[@"",@"",@];
//RAC 集合
// RACSequence *sequence = arr.rac_sequence;
// // 把集合转换成信号
// RACSignal *signal = sequence.signal;
// //订阅集合信号,内部会自动遍历所有的元素发出来
// [signal subscribeNext:^(id x) {
// NSLog(@"遍历数组%@",x);
// }];
//输出:
/**
遍历数组213
遍历数组321
遍历数组1
*/ //高级写法
[arr.rac_sequence.signal subscribeNext:^(id x) {
NSLog(@"高级写法遍历数组打印%@",x);
}]; //输出:
/**
高级写法遍历数组打印213
高级写法遍历数组打印321
高级写法遍历数组打印1
*/

字典

 NSDictionary *dict = @{@"sex":@"女",@"name":@"苍老师",@"age":@};

    //转换成集合
[dict.rac_sequence.signal subscribeNext:^(RACTuple *x) {
// NSString *key = x[0];
// NSString *value = x[1];
// NSLog(@"%@ %@",key,value);
// RACTupleUnpack:用来解析元组
// 宏里面的参数,传需要解析出来的变量名
//= 右边,放需要解析的元组
RACTupleUnpack(NSString *key, NSString *value) = x;
NSLog(@"%@ %@",key,value); }];
//输出:
/**
sex 女
name 苍老师
age 18
*/

字典转模型

NSString *filePath = [[NSBundle mainBundle] pathForResource:@"flags.plist" ofType:nil];
NSArray *dictArr = [NSArray arrayWithContentsOfFile:filePath]; // NSMutableArray *arr = [NSMutableArray array];
// // rac_sequence注意点:调用subscribeNext,并不会马上执行nextBlock,而是会等一会。 // [dictArr.rac_sequence.signal subscribeNext:^(NSDictionary *x) {
// Flag *flag = [Flag flagWithDict:x];
// [arr addObject:flag];
// }]; //高级用法
// map:映射的意思,目的:把原始值value映射成一个新值
// array: 把集合转换成数组
// 底层实现:当信号被订阅,会遍历集合中的原始值,映射成新值,并且保存到新的数组里。
NSArray *arr = [[dictArr.rac_sequence map:^id(NSDictionary *value) {
return [Flag flagWithDict:value];
}] array]; NSLog(@"%@",arr);

RACMulticastConnection:

RACMulticastConnection:用于当一个信号,被多次订阅时,为了保证创建信号时,避免多次调用创建信号中的block,造成副作用,可以使用这个类处理。

使用注意:RACMulticastConnection通过RACSignal的-publish或者-muticast:方法创建.

RACMulticastConnection简单使用:

RACMulticastConnection使用步骤:

1.创建信号 + (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe

2.创建连接 RACMulticastConnection *connect = [signal publish];

3.订阅信号,注意:订阅的不在是之前的信号,而是连接的信号。 [connect.signal subscribeNext:nextBlock]

4.连接 [connect connect]

RACMulticastConnection底层原理:

1.创建connect,connect.sourceSignal -> RACSignal(原始信号)  connect.signal -> RACSubject

2.订阅connect.signal,会调用RACSubject的subscribeNext,创建订阅者,而且把订阅者保存起来,不会执行block。

3.[connect connect]内部会订阅RACSignal(原始信号),并且订阅者是RACSubject

3.1.订阅原始信号,就会调用原始信号中的didSubscribe

3.2 didSubscribe,拿到订阅者调用sendNext,其实是调用RACSubject的sendNext

4.RACSubject的sendNext,会遍历RACSubject所有订阅者发送信号。

4.1 因为刚刚第二步,都是在订阅RACSubject,因此会拿到第二步所有的订阅者,调用他们的nextBlock

需求:假设在一个信号中发送请求,每次订阅一次都会发送请求,这样就会导致多次请求。

解决:使用RACMulticastConnection就能解决.

//1.创建信号
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"发送请求"); [subscriber sendNext:@""];
return [RACDisposable disposableWithBlock:^{ }];
}];
//2.订阅信号
[signal subscribeNext:^(id x) { NSLog(@"接收数据");
}];
[signal subscribeNext:^(id x) { NSLog(@"接收数据");
}]; // 3.运行结果,会执行两遍发送请求,也就是每次订阅都会发送一次请求 // RACMulticastConnection:解决重复请求问题
// 1.创建信号
RACSignal *connectionSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"发送请求--");
[subscriber sendNext:@]; return [RACDisposable disposableWithBlock:^{ }];
}]; //2.创建连接
RACMulticastConnection *connect = [connectionSignal publish]; //3.订阅信号
// 注意:订阅信号,也不能激活信号,只是保存订阅者到数组,必须通过连接,当调用连接,就会一次性调用所有订阅者的sendNext:
[connect.signal subscribeNext:^(id x) {
NSLog(@"订阅者一信号--");
}];
[connect.signal subscribeNext:^(id x) { NSLog(@"订阅者二信号--"); }];
//4.连接,激活信号
[connect connect]; //输出:
/**
发送请求--
订阅者一信号--
订阅者二信号--
*/

RACCommand

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

使用场景:监听按钮点击,网络请求

一、RACCommand使用步骤:

1.创建命令 initWithSignalBlock:(RACSignal * (^)(id input))signalBlock

2.在signalBlock中,创建RACSignal,并且作为signalBlock的返回值

3.执行命令 - (RACSignal *)execute:(id)input

二、RACCommand使用注意:

1.signalBlock必须要返回一个信号,不能传nil.

2.如果不想要传递信号,直接创建空的信号[RACSignal empty];

3.RACCommand中信号如果数据传递完,必须调用[subscriber sendCompleted],这时命令才会执行完毕,否则永远处于执行中。

三、RACCommand设计思想:内部signalBlock为什么要返回一个信号,这个信号有什么用。

1.在RAC开发中,通常会把网络请求封装到RACCommand,直接执行某个RACCommand就能发送请求。

2.当RACCommand内部请求到数据的时候,需要把请求的数据传递给外界,这时候就需要通过signalBlock返回的信号传递了。

四、如何拿到RACCommand中返回信号发出的数据。

1.RACCommand有个执行信号源executionSignals,这个是signal of signals(信号的信号),意思是信号发出的数据是信号,不是普通的类型。

2.订阅executionSignals就能拿到RACCommand中返回的信号,然后订阅signalBlock返回的信号,就能获取发出的值。

五、监听当前命令是否正在执行executing

六、使用场景,监听按钮点击,网络请求

//1.创建命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) { NSLog(@"执行命令");
//创建空信号,必须返回信号
//return [RACSignal empty]; //2.创建信号,用来传递数据
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { [subscriber sendNext:@"请求数据"]; //注意:数据传递完,最好调用sendCompleted,这时命令才执行完毕。
[subscriber sendCompleted]; return [RACDisposable disposableWithBlock:^{
NSLog(@"数据销毁");
}];
}];
}]; //强引用命令,不要被销毁,否则接收不到数据
_command = command; //监听事件有咩有完成
[command.executing subscribeNext:^(id x) {
if ([x boolValue] == YES) { // 当前正在执行
NSLog(@"当前正在执行");
}else{
// 执行完成/没有执行
NSLog(@"执行完成/没有执行");
} }]; //执行命令
[self.command execute:@];

RAC高级用法

    //创建信号中信号
RACSubject *signalOfSignals = [RACSubject subject];
RACSubject *signalA = [RACSubject subject];
RACSubject *signalB = [RACSubject subject];
// 订阅信号
// [signalOfSignals subscribeNext:^(RACSignal *x) {
// [x subscribeNext:^(id x) {
// NSLog(@"%@",x);
// }];
// }];
// switchToLatest:获取信号中信号发送的最新信号
[signalOfSignals.switchToLatest subscribeNext:^(id x) { NSLog(@"%@",x);
}]; // 发送信号
[signalOfSignals sendNext:signalA]; [signalA sendNext:@];
[signalB sendNext:@"BB"];
[signalA sendNext:@""];

RACCommand:处理事件

RACCommand:不能返回一个空的信号

  // 1.创建命令
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
// input:执行命令传入参数
// Block调用:执行命令的时候就会调用
NSLog(@"%@",input); return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) { // 发送数据
[subscriber sendNext:@"执行命令产生的数据"]; return nil;
}];
}]; // 如何拿到执行命令中产生的数据
// 订阅命令内部的信号
// 1.方式一:直接订阅执行命令返回的信号
// 2.方式二: // 2.执行命令
RACSignal *signal = [command execute:@]; // 3.订阅信号
[signal subscribeNext:^(id x) {
NSLog(@"%@",x);
}];




iOS开发ReactiveCocoa学习笔记(一)的更多相关文章

  1. iOS开发ReactiveCocoa学习笔记(六)

    RAC操作方法三. demo地址:https://github.com/SummerHH/ReactiveCocoa.git doNext deliverOn timeout interval del ...

  2. iOS开发ReactiveCocoa学习笔记(五)

    ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git filter ignore ignoreValu ...

  3. iOS开发ReactiveCocoa学习笔记(四)

    ReactiveCocoa常见操作方法介绍: demo地址:https://github.com/SummerHH/ReactiveCocoa.git 1.1 ReactiveCocoa操作须知 所有 ...

  4. iOS开发ReactiveCocoa学习笔记(二)

    RAC 中常见的宏: 使用宏定义要单独导入 #import <RACEXTScope.h> 一. RAC(TARGET, [KEYPATH, [NIL_VALUE]]):用于给某个对象的某 ...

  5. iOS开发ReactiveCocoa学习笔记(三)

    RAC常用用法: 1.监听按钮的点击事件: UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom]; button.frame ...

  6. 转:【iOS开发每日小笔记(十一)】iOS8更新留下的“坑” NSAttributedString设置下划线 NSUnderlineStyleAttributeName 属性必须为NSNumber

    http://www.bubuko.com/infodetail-382485.html 标签:des   class   style   代码   html   使用   问题   文件   数据 ...

  7. iOS开发如何学习前端(2)

    iOS开发如何学习前端(2) 上一篇成果如下. 实现的效果如下. 实现了一个横放的<ul>,也既iOS中的UITableView. 实现了当鼠标移动到列表中的某一个<li>,也 ...

  8. iOS开发如何学习前端(1)

    iOS开发如何学习前端(1) 我为何学前端?因为无聊. 概念 前端大概三大块. HTML CSS JavaScript 基本上每个概念在iOS中都有对应的.HTML请想象成只能拉Autolayout或 ...

  9. 关于iOS开发的学习

    关于iOS开发的学习,打个比方就像把汽车分解:    最底层的原料有塑料,钢铁    再用这些底层的东西造出来发动机,座椅    最后再加上写螺丝,胶水等,把汽车就拼起来了 iOS基本都是英文的资料, ...

随机推荐

  1. 多对多 hibernate映射

    数据库: create table EMPLOYEE ( EMPID NUMBER(6) not null, EMPNAME VARCHAR2(32) ) alter table EMPLOYEE a ...

  2. hadoop mapreduce 计算平均气温的代码,绝对原创

    1901 46 1902 21 1903 48 1904 33 1905 43 1906 47 1907 31 1908 28 1909 26 1910 35 1911 30 1912 16 1913 ...

  3. 如何从光盘本地安装CentOS 7图形界面(Gnome GUI)

    本例中通过在CentOS 7中修改repo文件,直接从光盘或者ISO镜像文件安装Gnome图形界面(Gnome GUI),从而避免耗时从官网或镜像下载. 1.首先确保光盘或者ISO镜像文件正确连接到客 ...

  4. Matlab数据类型的转换

    Matlab中有15种基本数据类型,主要是整型.浮点.逻辑.字符.日期和时间.结构数组.单元格数组以及函数句柄等. 1.整型:(int8:uint8:int16:uint16:int32:uint32 ...

  5. Matlab2012a下配置LibSVM—3.18

    1.下载最新版LibSVM 点击此处打开网页,点击zip file下载最新版的文件并解压放在任何目录下,建议放在安装目录便于查找.如我的文件解压在路径C:\ProgramFiles\MATLAB\R2 ...

  6. 第三天的 No session 问题

    1.1 No session(理解) 初始化快递员对象中 定区集合 Web层转Courier对象为json串时候,对象中有fixedareas集合属性,jpa集合属性加载策略延迟加载.在action中 ...

  7. 【jeasyui5】样式:调整页面显示的顶部菜单和左侧菜单

    1.顶部菜单修改:修改index2.js里面的InitTopMenu方法,将icon +2 2.左侧菜单宽度调整: 修改index.html,加上width:170的定长 <!-- 左侧菜单 - ...

  8. 13.Weblogic任意文件上传漏洞(CVE-2018-2894)复现

    Weblogic任意文件上传漏洞(CVE-2018-2894)复现 漏洞背景 WebLogic管理端未授权的两个页面存在任意上传getshell漏洞,可直接获取权限.两个页面分别为/ws_utc/be ...

  9. boost编译BUG

    linux GCC环境,boost在编译时未加-fPIC参数,导致如果有共享库使用boost静态库,会报如下错误:relocation R_X86_64_32 against `a local sym ...

  10. js判断IP字符串是否正确

    //判断ip地址的合法性 function checkIP(value){ var exp=/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0 ...