Reactive Cocoa

Functional Reactive Programming

Functional Reactive Programming(函数响应式(反应式)编程,以下简称FRP)是一种响应变化的编程范式,它能通过一种信号机制来记录值的变化,信号可以被叠加、分割或合并,通过对信号的组合,就可以对值进行监听,有点像数学中的各种公式。例如:

f(x) = x^2 + 2x + 1;

每当x变化时,f(x)的值也随之变化,可以看出,这个函数是由一个基本函数构成的,即:

f(x) = f1(x)^2;
f1(x) = x + 1;

数学中有个结论,大致的意思就是大部分的函数,无论多复杂,都能由可列个基本函数构成。简单说来,计算f(x)的过程就是先计算f1(x)的值,再带入f(x)中计算,这个计算过程就是FRP中的核心之一,事件流(Stream)。每当x发生变化时,f(x)的值理应响应这种变化,这是FRP的另外一个核心:属性(Properties)。

ReactiveCocoa

ReactiveCocoa是Github的一个开源的项目,是IOS平台上对FRP的实现。

RACStream

RACStream就对应FRP中的事件流,一个RACStream对象的意义相当于上面的f1(x)(每一个基本函数都有一个与之对应的RACStream对象),最后组合出来的函数f(x)则由RACStreamComponent表示。一个RACStream对象应有几个基本要素:

  1. 传入值
  2. 返回值
  3. 如何与其他函数组合
  4. 如何确定函数的作用域
  5. 函数名
在Objective C中,所有的对象都可以用id表示,多个值的组合可以用RACTurple(可以吧多个值压包和解包),1、2搞定;
 
函数名就叫RACStream对象的name就行,5解决;
 
由于两个函数组合之后还是一个函数,故两个RACStream对象组合后是一个新生成的Stream对象,它的输入是第一个Stream的输入值(或两个Stream的输入),返回值是第二个Stream运算后得到的值(或两个Stream的输出),RACStream中有两个函数,concat:和zipWith:能够将两个Stream对象组合起来,concat是将两个Stream串行连接起来,第二个Stream的输入值为第一个Stream的输出值(也就是说,只有当第一个Stream执行完才能执行第二个Stream)。zipWith是将两个Stream组合起来,当两个Stream都产生输出值时,组合成的Stream才输出值(RACTuple),其中任何一个Stream无法输出,组合成的Stream都无法输出,3解决;
 
RACStream中有一个bind:方法,是用来监测值和控制运行状态的。
 

RACSignal

信号,ReactiveCocoa中的核心,是一个主动信号流,它表示未来将要发送的数据,也可以将它当做一个“事件转发器”。一个signal和一个数据源绑定,当数据源的数据有更新时,signal会向signal所有的subscriber(订阅者)发送事件。

Signal只会向subscriber发送三种类型的事件:

    • Next:接受下一个事件
    • Error:事件发生错误,无法继续接受
    • Completed:完成接受,(textField中的值不会再改变或是用户不希望继续接受新的值)

一个Signal的生命周期内可以接受任意多个Next事件,一个Error事件或者是Completed事件(两种事件只可能出现一种)。

 

最有意思的是,Signals之间能够进行各种组合,具体说来,它可以被修改(Map)、过滤(Filter)、叠加(Combine)和串联(Chain)。

Map

Filter

1 [[self.textField.rac_textSignal filter:^BOOL(NSString*value) {    
2     return [value length]>= 3;   
3 }] subscribeNext:^(NSString*value) {    
4     NSLog(@"Text field has been updated: %@", value);   
5 >}];  

textField的text改变时发出的signal会先被进行过滤,只有当text的长度大于等于3时,才会被发到下一个接收方继续执行。

Combine

1 RAC(self,login.enabled) =
2         [RACSignal combineLatest:@[_userName.rac_textSignal,
3                                    _password.rac_textSignal]
4                           reduce:^(NSString *userName, NSString *password){
5                                return @(userName.length > 0 && password.length > 0);
6                            }];

 

userName和password的text改变的signal被联合在了一起,只有当两个text的长度都大于0时,login按钮的enable才为YES。 

 

RACSubscriber

subscriber(订阅者),接受signal发出的事件(next,complete,error)

@weakify(self);
    RACSignal *aSignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber){
        @strongify(self);
        [self doSomethingWithSuccessHandler:^{
            [subscriber sendNext:nil];
        }];
        return nil;
    }];
 
[aSignal subscribeNext:^(id x){
        NSLog(@"next event sended");
    }];

  

 

@weakify和@strongify的成对使用是确保在block中引用self不会引起循环引用的问题,这是RAC中定义的宏。

creatSignal函数创建了一个Signal,并定义了每次signal被订阅时需要做的事:doSomethingWithSuccessHandler:,这是一个异步的操作,当操作成功完成时,执行[subscriber sendNext:];告诉每一个订阅者,执行下一步操作。

注意,如果没有下面一句[aSignal subscribeNext:],则aSignal只会被创建,但是不执行block中的内容(doSomethingWithSuccessHandler:),这时signal没有一个订阅者,一般称之为冷信号(Cold)。而只有当一个Signal被订阅了以后([aSignal subscribeNext:]),才会执行block中的内容,这时signal有了一个订阅者,它会变成热信号(Hot),会执行creatSignal:后的block。当block中的内容执行到[subsriber sendNext:]时,所有的subscriber就会执行下一步的操作([aSignal subscribeNext:]后的block)。

副作用,一个信号(RACSignal)每添加一个订阅者(有一个对象执行[aSignal subscribeNext:]),creatSignal后的block就会被执行一次,这或许是你想要的,或许不是,可以写成[[RACSignal creatSignal:...] replay];避免多次执行creatSignal的block

 

RACScheduler

RACScheduler是ReactiveCocoa中对线程的简单封装,事件可以在指定的scheduler上分发执行,默认情况是事件都在一个默认的后台线程里面执行,如遇特殊情况需要在主线程调用,使用 deliverOn:可切换线程。

 

 

 

(未完待续...)

转载于:https://www.cnblogs.com/yybz/p/4031444.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值