ReactiveCocoa

目录

一、ReactiveCocoa结合了多种编程风格

二、在MVVM的项目中,为何总是会提到RAC呢?

三、ReactiveCocoa一些概念和用法


之前使用ReactiveCocoa+MVVM做过一次项目,当时使用它的原因就是感觉一个是新,一个是非常的简洁,看起来很酷。

当时别人问我ReactiveCocoa是什么,我都会回答:函数响应式编程。当时也不懂什么意思,那么现在再来回顾一下ReactiveCocoa。

一、ReactiveCocoa结合了多种编程风格:

函数式编程(Functional Programming)

函数式编程思想:是把操作尽量写成一系列嵌套的函数或者方法调用。

响应式编程(Reactive Programming)

响应式编程思想:不需要考虑调用顺序,只需要知道考虑结果,类似于蝴蝶效应,产生一个事件,会影响很多东西,这些事件像流一样的传播出去,然后影响结果。

那么我们简单举个例子:

RACSignal *signalA = RACObserve(self, a);

RACSignal *signalB = RACObserve(self, b);

RAC(self.label, text) = [RACSignal combineLatest:@[signalA,signalB] reduce:^(NSInteger countA, NSInteger countB){

return [NSString stringWithFormat:@"结果:%ld",countA+countB];

}];


那看看我们通常的做法

- (void)sumAction

{

self.label.text = [NSString stringWithFormat:@"结果:%ld",self.a+self.b];

}

看起来这个操作放在了一个方法中,挺简单的,可是每次调用self.a = X;或者self.b = Y;时,想要显示正确的值,那么必定要再调用一次[self sumAction];

当我们执行顺序为self.a = X; [self sumAction]; self.b = Y; 这个时候没有得到我们想要的X+Y的值,这不是响应式的。

如果我们将[self sumAction];的调用放在set方法中呢?那么确实是响应式的了,但用到了至少两个方法。

在MVVM的项目中,为何总是会提到RAC呢?

MVVM从MVC演化而来,为的是减少C的逻辑和业务。如果没有RAC,那么我们得使用NSNotification、delegate、KVO等将V变化产生的数据传给VM,从而改变M的值。NSNotification可能跨度大点,尤其delegate和KVO会免不了将业务再次放到C中,如果不放到C中,那么这个VM就会与这个View绑定得比较紧,而ViewModel有可能并不是只服务于特定的一个View,这样的话使用更加松散的绑定关系能够降低ViewModel和View之间的耦合度。

因此View一旦产生数据了扔信号扔给ViewModel,使用ReactiveCocoa更能体现其精髓。

注:大部分MVVM架构都会使用ReactiveCocoa,但是使用ReactiveCocoa的iOS应用不一定就是基于MVVM架构的。MVVM的关键是要有View Model。


三、ReactiveCocoa一些概念和用法:

1.map:

map是将一张表中的值映射新值到另一张表中

NSArray*mappedArray=[array rx_mapWithBlock:^id(ideach){

return @(pow([each integerValue], 2));

}];

注:这是建立了一个新的数组,而不是将原数组的值进行改变

这所对应的是

NSMutableArray*mutableArray=[NSMutableArrayarrayWithCapacity:array.count];

for(NSNumber*numberinarray){[mutableArrayaddObject:@(pow([number integerValue], 2))];

}

NSArray*mappedArray=[NSArrayarrayWithArray:mutableArray];

这就是更高级别的函数更占优势的地方

2.filter(过滤)

3.fold(结合)

//合数值

NSNumber*sum=[array rx_foldWithBlock:^id(id memo,id each){

return @([memo integerValue] +  [eachintegerValue]);

}];

//拼字符串

[[array rx_mapWithBlock:^id(id each){

return [each stringValue];

}] rx_foldInitialValue:@""block:^id(id memo,id each){

return [memo stringByAppendingString:each];

}];

4.Streams and Sequences

一系列的值抽象的被称为流,你可以认为流就像一个管道,其中值从一端进入从另一端放出。除非在管道的尾端当值出来时你能访问,访问过去的值甚至是当前值都是不可能的。没关系,我们拭目以待。

一系列的值,是吗?有点像一个列表,或在我们的例子中,一个数组。事实上,我们可以使用rac_sequence方法很容易地将一个NSArray转化成流。

NSArray*array=@[@(1),@(2),@(3)];

RACSequence*stream=[array rac_sequence];//将数组转化成流

[stream map:^id(id value){

return @(pow([value integerValue],2));

}];

NSLog(@"%@",[stream array]);//将流又转化成数组

//原来项目中使用了这个方法是这样的一个过程,map可以将字典转化成对象,然后就成为了对象的数组

NSLog(@"%@",[[[array rac_sequence] map:^id(idvalue){

return @(pow([value integerValue],2));

}] array]);

ReactiveCocoa包含left fold和right fold。left

fold是从开始往结尾穿过一个数组,right fold是反的

5.Signals

信号是另一种类型的流,对比sequences(序列),信号是推驱动,新值通过管道推压并且不能拉出,在以后他们把将被递送的数据抽取出来。

信号包括三种不同的类型值:Next、Error、Completion.

值得注意的是,Error和Completion只能通过信号送出一次,并且只有一个被送出。

ReactiveCocoa_第1张图片

6.Subscriptions

[self.textField.rac_textSignal subscribeNext:^(id x){

NSLog(@"New value:%@", x);

}error:^(NSError*error){

NSLog(@"Error: %@",error);

}completed:^{

NSLog(@"Completed.");

}];

在textfield中输入字符时,发现信号不发送error值和当信号deallocated的时候发送completion值,因此我们可以

[self.textField.rac_textSignal subscribeNext:^(idx){

NSLog(@"New value: %@", x);

}];

当subscribe(订阅)一个信号,将自动创建一个subscription对象,这个对象将自动保留,同时也保留了这个信号的subscribing状态,你可以手动处理订阅者,但这不是典型的操作,一般在使用重用视图的时候处理信号

7.Deriving State

宏RAC()有两个参数:一个对象和该对象的键值路径。然后,它执行一个单向的绑定右边的值到键值路径。值必须是对象,这就是为什么我们把布尔值包装成一个NSNumber。

RACSignal *validEmailSignal=[self.textField.rac_textSignalmap:^id(NSString *value){

return @([value rangeOfString:@"@"].location!= NSNotFound);

}];

RAC(self.button,enabled)=validEmailSignal;

RAC(self.textField,textColor)=[validEmailSignalmap:^id(id value){

if ([value boolValue]) {           return [UIColor greenColor];

} else {

return [UIColor redColor];

}

}];

8.Commands

当你想要发送一个信号的值对用户交互事件响应,Commands是有用的。command的信号能被订阅来稍后接收返回的输出信号。

self.button.rac_command= [[RACCommand alloc]initWithEnabled:validEmailSignal

signalBlock:^RACSignal *(idinput){

NSLog(@"Button was pressed.");

return [RACSignal empty];

}];

这个按钮将保持disabled直到信号返回complete值(也可以是空值([RACSignal empty]))

9.RACSubject

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

创建方法:

(1)创建RACSubject

(2)订阅信号

(3)发送信号

工作流程:

(1)订阅信号时,内部保存了订阅者,和订阅者响应block

(2)当发送信号的,遍历订阅者,调用订阅者的nextBlock

注:如果订阅信号,必须在发送信号之前订阅信号,不然收不到信号,这也有别于RACReplaySubject

-(void)racSubjectTest

{

RACSubject *subject= [RACSubjectsubject];

[subject subscribeNext:^(idx) {

NSLog(@"1

%@,type:%@",x,NSStringFromClass(object_getClass(x)));

}];

[subject subscribeNext:^(idx) {

NSLog(@"2

%@,type:%@",x,NSStringFromClass(object_getClass(x)));

}];

[subjectsendNext:@1];

[subject subscribeNext:^(idx) {

NSLog(@"3

%@,type:%@",x,NSStringFromClass(object_getClass(x)));

}];

}

10.Hot and Cold Signals

信号通常是懒惰的,意味着它们只有在有人已经订阅它们的时候,它们才会工作和发送信号。每增加一个订阅,工作就会重新执行。对于繁琐的操作,这是可以接受的,并且实际上这是所希望的。在ReactiveCocoa术语中,这种类型的信号被称为“冷信号”。

有时,我们想工作立即被执行,这种类型的信号被称为“热信号”。热信号非常罕见被使用到。

11.Multicasting

Multicasting是指某个信号订阅被共享于大量订阅者的术语。信号,一般是“冷信号”,它有时是不可取的,执行工作每次它都是订阅的“冷信号”,这常常是当副作用或工作时订阅是昂贵的时候执行,否则只有在适当的时候才会执行。网络请求浮现在脑海中。

于是我们创建来源于信号的RACMulticastConnection。你可以在RACSignal中使用publish方法或multicast:方法。前一种方法为您创建一个multicast的连接。后一种方法也做了同样的事,但还采用RACSubject参数。这个subject是手动从底层信号发送值,每当它被调用。然后,任何由底层信号发送的值有兴趣订阅连接的信号,相反(如果你提供一个subject,信号正好是这个subject)。

ReactiveCocoa_第2张图片

由于在默认情况下信号是“冷信号”,每添加一个订阅者,则它的工作被执行。如果是这样的话,那是不可取的,我们使用multicast的连接。

ReactiveCocoa_第3张图片

multicast的连接订阅的信号,当它已经通过了新的值,发送这些值到信号(这作为一个公共属性公开)。你可以多次订阅这个信号,并且在订阅时执行的工作,只是一次

你可能感兴趣的:(ReactiveCocoa)