ReactiveCocoa

• 知识备用库:iOS10 collectionView以前是复用的,但是iOS10以后就要设置一个属性才可以复用。

• C语言面向过程编程,OC面向对象编程

• 链式编程思想(masonry),可读性特别好,聚合度高;

• 响应式编程思想(KVO),解耦比较好;

• 函数式编程结构 + 响应式编程思想 = ReactiveCocoa 的实现;

• 所谓的店语法无非是get或者set方法,set方法一般用于赋值,get方法一般用于取值

• 关于OC中的括号传递参数的思想其本质就是block,当想用链式编程思想的时候第一个想到的就是block。下面是一个简单的例子:

```

-(void(^)())testCode

{

NSLog(@"======a");

void(^block)() = ^{

NSLog(@"======c");

};

NSLog(@"======b");

return block;

}

调用后的输出的结果为:

2018-02-05 15:54:02.660583<0800 RACtest[1138:36428] ======a

2018-02-05 15:54:02.660738>0800 RACtest[1138:36428] ======b

2018-02-05 15:54:02.660823+0800 RACtest[1138:36428] ======c

```

---

```

^{

//其实这里你可以把代码可以全部保存到这里

NSLog(@"保存你的代码");

}();

```

• 备注:其实上面的这种代码格式为了更好的管理代码,没别的意思

• 下面我就用这种链式编程思想实现一个简单的加法计算器,仿照Masonry,首先我在这里定义了一个 CalculateManager的类,然后在里面用block实现链式调用的这种方法

```

typedef CalculateManager *(^CalculateManagerBlock)(NSInteger value);

-(CalculateManagerBlock)add

{

return ^(NSInteger value){

_result += value;

return self;

};

}

```

• 上面的这种思想我觉得在封装SDK的时候真心是一个不错的选择,但是你知道这个函数的执行顺序吗,首先调用函数方法,然后调用block,记住这个参数不是函数本身的参数,而是block的,调用()之心里面的 ^{}里面的代码,然后在block里返回CalculateManager。

• 上面是RAC的基础下面进入正题:

• 先说下KVO,其实KVO是属于响应式编程思想的一种,KVO底层的原理是监听set方法有没有调用,KVO只能监听set方法。KVO的实现原理,(1)动态生成子类,也叫派生类(子类会继承所有的父类的方法);(2)生成子类为了重写父类的set方法,目的监听属性有没有改变;(3)修改对象的isa指针,isa指向那个类就去那个类中查找方法,IPM-指向实际执行函数体的函数指针,_cmd SEL 类型的一个变量,Objective C的函数的前两个隐藏参数为self 和 _cmd,ivar ivar - objective C中的实例变量。

• RAC有两个版本一个是OC的,另外一个是swift的,默认是swift版本的,如果想要OC版本的用 ReactiveObjC 搜索。

• 在这里任何事情都通过信号传递,当然新框架的学习也是非常困惑的,我们一般时候学习的时候通常在一般的常用的类入手,其次想着框架的实现和源码的阅读,对于RAC来说,在代码的实现是函数式,其思想是响应式编程。

• RACSignal:基础的类

• RACDisposable:处理数据

• RACSubscriber:订阅者

• RAC注意点,在信号订阅的时候不要订阅多次,不要分开订阅,下面是关于RAC的执行流程

• //如何事件都是信号,信号需要去订阅(可以理解为绑定)

• 信号的订阅简单的实现(方法1)

```

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

NSLog(@"=======执行1");

[subscriber sendNext:@2];

[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{

//清空数据

NSLog(@"=======执行5");

}];

}];

[signal subscribeNext:^(id  _Nullable x) {

NSLog(@"=======执行2");

} error:^(NSError * _Nullable error) {

NSLog(@"=======执行3(并行执行)");

} completed:^{

NSLog(@"=======执行3");

}];

```

输出结果:

```

2018-02-05 21:34:04.154012<0800 RACtest[891:27500] =======执行1

2018-02-05 21:34:04.154145>0800 RACtest[891:27500] =======执行2

2018-02-05 21:34:04.154282<0800 RACtest[891:27500] =======执行3

2018-02-05 21:34:04.154384>0800 RACtest[891:27500] =======执行5

```

• • 想订阅多次的话可以用下面的方法代替上面的流程(方法二)

```

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

NSLog(@"=======执行1");

[subscriber sendNext:@2];

[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{

//清空数据

NSLog(@"=======执行5");

}];

}];

//订阅信号传值

[signal subscribeNext:^(id  _Nullable x) {

NSLog(@"=======subscribeNext");

}];

//订阅信号的错误信息

[signal subscribeError:^(NSError * _Nullable error) {

NSLog(@"=======subscribeError");

}];

//信号完成

[signal subscribeCompleted:^{

NSLog(@"=======subscribeCompleted");

}];

```

输入结果:

```

2018-02-05 21:36:40.271006<0800 RACtest[958:29805] =======执行1

2018-02-05 21:36:40.271150>0800 RACtest[958:29805] =======subscribeNext

2018-02-05 21:36:40.271328<0800 RACtest[958:29805] =======执行5

---------------------------------------------------------------

2018-02-05 21:36:40.271509>0800 RACtest[958:29805] =======执行1

2018-02-05 21:36:40.271605<0800 RACtest[958:29805] =======执行5

---------------------------------------------------------------

2018-02-05 21:36:40.271804>0800 RACtest[958:29805] =======执行1

2018-02-05 21:36:40.271981<0800 RACtest[958:29805] subscribeCompleted=======

2018-02-05 21:36:40.272084>0800 RACtest[958:29805] =======执行5

```

• 上面都能实现信号的订阅,但是方法1的 block 会多次   =======执行1和 =======执行5

• RACSubject 先订阅然后可以发送和上一个 RACSignal 完全不同,这个类订阅多次然后发送的时候一起执行订阅者的信息。

```

RACSubject *subject = [RACSubject subject];

[subject subscribeNext:^(id  _Nullable x) {

NSLog(@"订阅者1");

}];

[subject subscribeNext:^(id  _Nullable x) {

NSLog(@"订阅者2");

}];

[subject sendNext:@1];

```

• 输出打印信息:

```

2018-02-05 21:53:50.012831<0800 RACtest[1044:37676] 订阅者1

2018-02-05 21:53:50.012988>0800 RACtest[1044:37676] 订阅者2

```

• 以上都是先订阅后发送信号

• 下面是一个先发送后订阅的一个类

• RACReplaySubject 继承自 RACSubject

```

RACReplaySubject *reSubject = [RACReplaySubject subject];

[reSubject sendNext:@"====1"];

[reSubject sendNext:@"====2"];

[reSubject subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}];

```

• 输出结果:

```

2018-02-05 21:59:14.855276<0800 RACtest[1076:40729] ====1

2018-02-05 21:59:14.855430>0800 RACtest[1076:40729] ====2

```

• 关于RAC的类 RACSubject 在开发中的应用,可以代替代理,如果有这样一个需求,在VC上点击几个 view 视图 ,每点击一个就执行一次订阅者,还有一个需求就是我点击了 view 点击无论多少次只是执行一次 订阅,对于 RACSubject来说只是你调用 sendNext 和 sendCompleted的选择,感觉特爽,再也不用判断各种状态了;后者只需要调用 sendNext 和 sendCompleted,前者调用 sendNext 就可以实时的获取到状态。

=======================第一天结束====================

• 开启异步做一些事情处理数据,关于tableView的数据模型很复杂的时候可以开启异步线程再去刷新表格。

• RAC的集合:处理数据在子线程中,异步线程处理数据,一般OC的数据类型转为RAC的集合的方式调用 RACSequence 这个类型的属性就可以了;例如 :下面方法可以遍历数组,当打印线程的时候在子线程中,不能在当前线程刷新UI

• • 数组的遍历方式,有两种方式

• 下面是方法1,也是我们常用的

```

[arrays.rac_sequence.signal subscribeNext:^(id  _Nullable x) {

//不能更新UI

NSLog(@"%@",[NSThread currentThread]);

NSLog(@"订阅信号");

}];

```

• 下面是方法二:RAC中精简版的

//映射,value就是集合;返回值:需要映射成那个值

```

arrM = [[arrays.rac_sequence map:^id _Nullable(id  _Nullable value) {

return [FlagItem itemWithDict:value];

}] array];

//arrM 是用来装模型的数组,里面有我们想要的转换的模型,arrays是一个json串

```

====================数组======================

• • 字典的遍历

```

NSDictionary *dict = @{

@"name":@"wagner",

@"money":@123456

};

//x 代表一个元组,在这里元组可以当做是数组来使用

//RACTupleUnpack 把元组解析成通用的值类型

[dict.rac_sequence.signal subscribeNext:^(id  _Nullable x) {

RACTupleUnpack(NSString *key, id value) = x;

NSLog(@"%@ === %@",key,value);

}];

* 输出打印:

```

2018-02-06 09:59:45.931174<0800 RACtest[2076:105197] name===wagner

2018-02-06 09:59:45.932966>0800 RACtest[2076:105197] money===123456

```

* ==================

* 关于 RACMulticastConnection的讲解

```

//    RACMulticastConnection :(RACSignal) 用于一个信号的订阅,被订阅多次,为了防止多次调用创建信号中的block,造成负面影响可以使用这个类做处理

@weakify(self);

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

@strongify(self);

//网络数据请求,完毕了传递过去,loadData就是一个简单的网络请求函数

[self loadData:^(id data) {

[subscriber sendNext:data];

}];

return [RACDisposable disposableWithBlock:^{

}];

}];

#pragma mark - 多次订阅怎么办

[signal subscribeNext:^(id  _Nullable x) {

//接收数据刷新页面

}];

[signal subscribeNext:^(id  _Nullable x) {

//接收数据刷新页面

}];

```

* 解决以上方案的办法,订阅多次进行多次网络请求

```

//    RACMulticastConnection :(RACSignal) 用于一个信号的订阅,被订阅多次,为了防止多次调用创建信号中的block,造成负面影响可以使用这个类做处理

@weakify(self);

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

NSLog(@"网络数据发送请求");

@strongify(self);

//网络数据请求,完毕了传递过去,loadData就是一个简单的网络请求函数

[self loadData:^(id data) {

[subscriber sendNext:data];

}];

return [RACDisposable disposableWithBlock:^{

}];

}];

//把信号转为 RACMulticastConnection 形式了

RACMulticastConnection *cnt = [signal publish];

//下来订阅的方法

[cnt.signal subscribeNext:^(id  _Nullable x) {

}];

[cnt.signal subscribeNext:^(id  _Nullable x) {

}];

[cnt connect];

```

* RACMulticastConnection 的原理:RACSignal 创建信号的时候用 RACDynamicSignal 保存了block ;这个类其实和RACSubject类的使用差不多是一样的。

* 理解和思想说明:我觉得学到这里,个人感觉RAC的这三个类的作用的本质就是保存block,在适当的地方执行block,RACSubject 、 RACSignal 、RACMulticastConnection 三个类互相和做完成数据流的互换,看你想用那种形式的数据流的选择,然后在其中选择两个类互相使用就可以完美解决问题,这是我这段时间学习RAC这三个类的见解,有什么不对的地方还望大家伙见谅和提出。

=================RACCommand==============

* 关于 RACCommand 的讲解 : RAC用于处理事件的类

```

RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {

//command 的block

NSLog(@"%@",input);

NSLog(@"====1");

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

//RACSignal 的 block

NSLog(@"====2");

[subscriber sendNext:@"hello"];

[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{

//RACDisposable 的 block

NSLog(@"====end");

}];

}];

}];

//只要执行 execute 就会执行上面的 command 的 block

//[command execute:@1] 这里会返回一个信号就是上面的 RACCommand 里面返回的那个 block

[[command execute:@1] subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}];

• 输出结果:

```

2018-02-06 11:27:53.451167<0800 RACtest[2441:151535] 1

2018-02-06 11:27:53.451340>0800 RACtest[2441:151535] ====1

2018-02-06 11:27:53.457199<0800 RACtest[2441:151535] ====2

2018-02-06 11:27:53.457458>0800 RACtest[2441:151535] hello

2018-02-06 11:27:53.457807+0800 RACtest[2441:151535] ====end

```

• RACCommand 中的 executionSignals 的block返回的还是信号,executionSignals 的 switchToLatest 表示最近一次发送的信号;executing 监听命令有没有完成,可以在 executing 监听的block中获取,executing有一个 skip 方法可以跳过第一次(一般时候我们跳过第一次),最后一次当我们使用完订阅者后必须发送一个 [subscriber sendCompleted] 。

• 下面是一个使用 RACCommand 的例子,判断状态的

```

RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {

//command 的block

NSLog(@"%@",input);

NSLog(@"====1");

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

//RACSignal 的 block

NSLog(@"====2");

[subscriber sendNext:@"hello"];

[subscriber sendCompleted];

return [RACDisposable disposableWithBlock:^{

//RACDisposable 的 block

NSLog(@"====end");

}];

}];

}];

[[command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {

if ([x boolValue]) {

NSLog(@"正在执行");

}else{

NSLog(@"执行完毕");

}

}];

[command execute:@1];

```

• 输出结果:

```

2018-02-06 14:10:55.555576<0800 RACtest[2733:176933] 1

2018-02-06 14:10:55.555752>0800 RACtest[2733:176933] ====1

2018-02-06 14:10:55.561215<0800 RACtest[2733:176933] 正在执行

2018-02-06 14:10:55.561410>0800 RACtest[2733:176933] ====2

2018-02-06 14:10:55.562035<0800 RACtest[2733:176933] ====end

2018-02-06 14:10:55.562554>0800 RACtest[2733:176933] 执行完毕

```

• 下面是 RACCommand 按钮的使用

• 按钮的 RAC 使用的方法1

```

_loginBtn.rac_command = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {

//command 的 block

NSLog(@"按钮的点击");

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

//信号的 block ,没有涉及传递的话一般时候没啥作用

[subscriber sendNext:input];

return nil;

}];

}];

[_loginBtn.rac_command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {

NSLog(@"点击后要发送的值");

}];

```

• 按钮的 RAC 使用的方法2-----当想用状态控制按钮的状态的时候一般用下面的方法

//这个用于控制按钮的状态

RACSubject *subjectEnable = [RACSubject subject];

```

_loginBtn.rac_command = [[RACCommand alloc] initWithEnabled:subjectEnable  signalBlock:^RACSignal * _Nonnull(id  _Nullable input) {

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

[subscriber sendNext:input];

//请求数据核对完毕后调用

[subscriber sendCompleted];

return nil;

}];

}];

//每次监听按钮的状态

[[_loginBtn.rac_command.executing skip:1] subscribeNext:^(NSNumber * _Nullable x) {

[subjectEnable sendNext:@(![x boolValue])];

}];

[_loginBtn.rac_command.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {

NSLog(@"点击后要发送的值");

}];

```

• 上面如果想让按钮点击一次之后不能再次点击最简单的做法是修改[subjectEnable sendNext:@(NO)]; 就可以了

=====================RAC 处理事件======================

下面是一个判断相应方法的例子:

```

+(instancetype)allocWithZone:(struct _NSZone *)zone

{

ViewController *vc = [super allocWithZone:zone];

[[vc rac_signalForSelector:@selector(viewDidLoad)] subscribeNext:^(RACTuple * _Nullable x) {

NSLog(@"viewDidLoad");

}];

[[vc rac_signalForSelector:@selector(viewWillAppear:)] subscribeNext:^(RACTuple * _Nullable x) {

NSLog(@"viewWillAppear");

}];

return vc;

}

```

• 关于 RAC 中KVO的使用,其中 keypath 是一个宏,在使用的时候前面记得加@ ,移除的话用OC的移除方法就可以了

• 方法1

```

[[self rac_valuesForKeyPath:@keypath(self,age) observer:self] subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}];

```

• 方法2,简洁的写法

```

[RACObserve(self, age) subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}];

```

• 关于按钮的点击事件的监听方法

//RAC 监听,subscribeNext 订阅信号

```

[[_loginBtn rac_signalForControlEvents:UIControlEventTouchUpInside] subscribeNext:^(__kindof UIControl * _Nullable x) {

NSLog(@"%@",x);

}];

```

• 关于RAC 实现通知

```

NSString *notificationName = @"note";

//监听通知,观察者不需要去管理,RAC 内部管理你的通知,你可以管理也可以不用管理

[[[NSNotificationCenter defaultCenter] rac_addObserverForName:notificationName object:nil] subscribeNext:^(NSNotification * _Nullable x) {

NSLog(@"监听到了通知");

}];

[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:nil];

```

• RAC 事件绑定

• 方法1,输入框变化,lable的文字也会变化

```

[_testField.rac_textSignal subscribeNext:^(NSString * _Nullable x) {

NSLog(@"%@",x);

_lable.text = x;

}];

```

• • 2

```

RAC(_lable,text) = _testField.rac_textSignal;

```

• 当一个页面发送多个请求的时候

//创建一个请求最热数据的信号

```

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

[subscriber sendNext:@"最热"];

return [RACDisposable disposableWithBlock:^{

}];

}];

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

[subscriber sendNext:@"最新"];

return [RACDisposable disposableWithBlock:^{

}];

}];

//Selector 当数组中的信号都发送一次 next 的时候就会调用 Selector 方法

//使用的时候, Selector 要求有几个信号就有几个参数

[self rac_liftSelector:@selector(updateUIHotData:newData:) withSignalsFromArray:@[hotSignal,newSignal]];

```

• RAC + MVVM

/*框架思想:把业务划分清楚,使得代码更加的好维护;本质就是抽离不同层次的代码

MVC:

MVC S(业务类)

MVVM :2015

VIPER :2014(美团)

V:view

I:交互

P:展示

E:model

R:路由,也就是跳转,在iOS中也就是(push present)

*/

=====================RAC的hook======================

• hook截获API的思想

• ReactiveCocoa的操作核心 bind :绑定特点的属性,然后在block里面用 RACReturnSignal 返回修改的结果即可。

```

//需求:每次只要文本框的改变  + HWW ,函数式编程思想

[[_textField.rac_textSignal bind:^RACSignalBindBlock _Nonnull{

//信号改变就一会调用,并且把值传递过来

NSLog(@"bind");

return ^RACSignal *(id value, BOOL *stop){

NSLog(@"旧值==%@",value);

NSString *result = [NSString stringWithFormat:@"%@HWW",value];

return [RACRet urnSignal return:result];

};

}] subscribeNext:^(id  _Nullable x) {

NSLog(@"传递过来的新值:%@",x);

}];

```

• 关于flatterMap的用法

• Map是值的变化,flattenMap 是信号的变化

```

//对原数据的处理

[[_textField.rac_textSignal flattenMap:^__kindof RACSignal * _Nullable(NSString * _Nullable value) {

value = [value stringByAppendingString:@"hhee"];

return [RACReturnSignal return:value];

}] subscribeNext:^(id  _Nullable x) {

NSLog(@"===%@",x);

}];

// map方法

[[_textField.rac_textSignal map:^id _Nullable(NSString * _Nullable value) {

return [value stringByAppendingString:@"你好"];

}] subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}] ;

```

• 上面的代码可以看出Map和 flattenMap的区别,两个的区别在于一个返回的是 string类型,二另外一个是 RACReturnSignal类型。

• 下面是信号中的信号的例子:

```

RACSubject *signalOfSingal = [RACSubject subject];

RACSubject *signal = [RACSubject subject];

[signalOfSingal subscribeNext:^(id  _Nullable x) {

[x subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}];

}];

[signalOfSingal sendNext:signal];

[signal sendNext:@1];

```

• 使用flatten后关于信号中的信号的例子,和上一个例子一样

```

RACSubject *signalOfSingal = [RACSubject subject];

RACSubject *signal = [RACSubject subject];

[[signalOfSingal flattenMap:^__kindof RACSignal * _Nullable(id  _Nullable value) {

return value;

}] subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}];

[signalOfSingal sendNext:signal];

[signal sendNext:@1];

```

• 关于concat的用法,按照顺序执行的话用这个函数方法,但是注意的一点是,前一个信号sendCompleted 后,后面一个信号才可以执行。

• 场景:有可能两个信号当订阅的时候,需要用两次订阅,下面的这种形式,输出的答案但我们想要A和B的顺序,下面这种方案是实现不了的

```

RACSubject *signalA = [RACSubject subject];

RACSubject *signalB = [RACReplaySubject subject];

NSMutableArray *arrayM = [NSMutableArray array];

//RACSubject 先订阅再发送,不想有顺序可以用RACReplaySubject

[signalA subscribeNext:^(id  _Nullable x) {

[arrayM addObject:x];

}];

[signalB subscribeNext:^(id  _Nullable x) {

[arrayM addObject:x];

}];

[signalB sendNext:@"B"];

[signalA sendNext:@"A"];

//打印数组的值

NSLog(@"%@",arrayM);

```

• 输出是:

```

(

B,

A

)

```

• 方法2,解决上面的问题

```

RACSubject *signalA = [RACSubject subject];

RACSubject *signalB = [RACReplaySubject subject];

NSMutableArray *arrayM = [NSMutableArray array];

[[signalA concat:signalB] subscribeNext:^(id  _Nullable x) {

[arrayM addObject:x];

}];

//发送信号

[signalB sendNext:@"B"];

[signalA sendNext:@"A"];

[signalA sendCompleted];

//打印数组的值

NSLog(@"%@",arrayM);

```

• 输出的结果:

```

(

A,

B

)

```

• then的用法

• then和concat的区别,then一般用于当网络请求的时候,第一次网络请求完毕,再请求别的数据,例如类别和详情数据的展示

```

//解决请求网络类别,然后在请求其他详情的数据嵌套问题。这个方法在另外一个方法重调用

[[[self loadCategoryData] then:^RACSignal * _Nonnull{

return [self loadDetailData];

}] subscribeNext:^(id  _Nullable x) {

//显示详情数据

}];

// 下面两个方法是辅助作用

//类别详情数据和类别的请求,只是写了函数,没写实现

-(RACSignal *)loadDetailData

{

RACReplaySubject *subject = [RACReplaySubject subject];

//发送网络请求

[subject sendNext:@"detail"];

return subject;

}

//分类数据

-(RACSignal *)loadCategoryData

{

RACReplaySubject *subject = [RACReplaySubject subject];

//发送网络请求

[subject sendNext:@"category"];

[subject sendCompleted];

return subject;

}

```

• 注意点:关于网络数据请求用到多个信号处理数据的时候一般都用 RACReplaySubject 不用 RACSubject ,这个因为RACSubject 是必须先订阅后发送,而 RACReplaySubject则可以先发送后订阅和先后顺序没关系。

• 关于 merge,只要任何一个发送数据就能订阅,无序的,可以处理来自两个不同信号的请求数据,在同一个方法中进行统一的处理操作;concat 只有当定一个订阅者发送完毕后第二个才能订阅发送数据;then 第一个订阅者发送完毕,而且只能接收到最后一个订阅者发送的数据。

```

RACSubject *signalA = [RACSubject subject];

RACSubject *signalB = [RACSubject subject];

[[signalA merge:signalB] subscribeNext:^(id  _Nullable x) {

NSLog(@"%@",x);

}];

[signalB sendNext:@"B"];

[signalA sendNext:@"A"];

```

• 关于zipWith,压缩同时要有数据,不然不会发送数据。结果是一个元组

```

//zipWith:合并

RACSubject *signalA = [RACSubject subject];

RACSubject *signalB = [RACSubject subject];

[[signalA zipWith:signalB] subscribeNext:^(id  _Nullable x) {

RACTupleUnpack(NSString *a,NSString *b) = x;

NSLog(@"%@=%@",a,b);

}];

[signalB sendNext:@"B"];

[signalA sendNext:@"A"];

```

ReactiveCocoa的学习内容的更多相关文章

  1. ReactiveCocoa基础知识内容

    本文记录一些关于学习ReactiveCocoa基础知识内容,对于ReactiveCocoa相关的概念如果不了解可以网上搜索:RACSignal有很多方法可以来订阅不同的事件类型,ReactiveCoc ...

  2. web前端开发学习内容

    应该 具备的 知识技能 :懂web标准,熟练手写 xhtml css3 并符合 符合w3c标准                       代码能 兼容主流浏览器.ie6.7.8.9 ff 等.    ...

  3. STM32学习内容和计划

    一.STM32学习内容(流程) 1.学习STM32开发流程 ①MDK使用.建立工程.调试等 ②库开发方法 2.学习STM32常用外设开发 ①GPIO ②中断 ③定时器 ④串口 ⑤CAN 3.学习STM ...

  4. u-boot代码学习内容

    前言  u-boot代码庞大,不可能全部细读,只能有选择的读部分代码.在读代码之前,根据韦东山教材,关于代码学习内容和深度做以下预先划定. 一.Makefile.mkconfig.config.mk等 ...

  5. 2.Freshman阶段学习内容的确定

    我刷知乎.在知乎上答题的程序员,不是很牛逼就是更牛逼,说起各种系统.各种系统的各种版本.各种语言.数据库.算法.IT届的各种圣战都有板有眼.信手拈来.头头是道,不得不服.这导致了一些非常严重的问题:我 ...

  6. 20155209 2016-2017-2 《Java程序设计》第九周学习总结 ## 教材学习内容总结

    教材学习内容总结 JDBC API 允许用户访问任何形式的表格数据,尤其是存储在关系数据库中的数据. 执行流程: •连接数据源,如:数据库. •为数据库传递查询和更新指令. •处理数据库响应并返回的结 ...

  7. JAVA第十周《网络编程》学习内容总结

    JAVA第十周<网络编程>学习内容总结 学习内容总结 1.初听到网络编程四个字可能会觉得很困难,实际上网络编程就是在两个或两个以上的设备(例如计算机)之间传输数据,把数据发送到指定的位置, ...

  8. # 20145118 《Java程序设计》第4周学习总结 ## 教材学习内容总结

    20145118 <Java程序设计>第4周学习总结 教材学习内容总结 本周内容为教材第六.七两张内容. 重点概念: 1.面向对象中,子类继承父类,避免重复的行为定义,是一种简化操作. 2 ...

  9. 20145118 《Java程序设计》第5周学习总结 教材学习内容总结

    20145118 <Java程序设计>第5周学习总结 教材学习内容总结 1.Java中所有错误都会被打包成对象,可以通过try.catch语法对错误对象作处理,先执行try,如果出错则跳出 ...

随机推荐

  1. vue踩坑记

    vue踩坑记 易错点 语法好难啊qwq 不要把'data'写成'date' 在v-html/v-bind中使用vue变量时不需要加变量名 在非vue事件中使用vue中变量时需要加变量名 正确 < ...

  2. speech

    1.李开复:一个人的成功,15%靠专业知识,其余15%人际沟通,公众演讲,以及影响他人的能力 2.演讲是一门遗憾的艺术 3.没有准备就等于准备失败 4.追求完美,就是在追求完蛋 5.宁可千日无机会,不 ...

  3. 【我的前端自学之路】【HTML5】Web Socket

    以下为自学笔记内容,仅供参考. 转发请保留原文链接:https://www.cnblogs.com/it-dennis/p/10508118.html 什么是Web Socket WebSocket ...

  4. error_Could not load file or assembly

    原文链接 Could you be missing the loaded assembly from your configuration file? Ensure you have somethin ...

  5. Vivado HLS 工具

    干什么的 Vivado HLS工具可以将C语言高级综合为硬件. 为什么要使用HLS 可以在更高的抽象层次描述功能,而不是在传统的RTL级别 一个潜在的用处是,系统设计划分成硬件部分和软件部分之后,软件 ...

  6. HDU 6521 Party

    6521 思路: 线段树玄学剪枝, 俗称吉司机线段树. 代码: #pragma GCC optimize(2) #pragma GCC optimize(3) #pragma GCC optimize ...

  7. hadoop之数据压缩与数据格式

    * 注:本文原创,转载请注明出处,本人保留对未注明出处行为的责任追究. a.数据压缩 优点: 1.节省本地空间 2.节省带宽 缺点: 花时间 1.MR中允许进行数据压缩的地方有三个: 1)input起 ...

  8. RESTful协议

    目的:在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强.性能好.适宜通信的架构 解决问题:如何开发在互联网环境中使用的软件,实现网站即软件 涉及主要元素 资源(Res ...

  9. pip使用国内源

    对于Python开发用户来讲,PIP安装软件包是家常便饭.但国外的源下载速度实在太慢,浪费时间.而且经常出现下载后安装出错问题.所以把PIP安装源替换成国内镜像,可以大幅提升下载速度,还可以提高安装成 ...

  10. 【细小碎的oi小知识点总结贴】不定时更新(显然也没人看qwq)

    1.memcpy: 从a数组中复制k个元素到b数组: memcpy(b,a,sizeof(int)*k); #include<cstring> #include<iostream&g ...