Reactive Cocoa

Functional Reactive Programming

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

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

每当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对象应有几个基本要素:spa

  1. 传入值
  2. 返回值
  3. 如何与其余函数组合
  4. 如何肯定函数的做用域
  5. 函数名
在Objective C中,全部的对象均可以用id表示,多个值的组合能够用RACTurple(能够吧多个值压包和解包),一、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发送三种类型的事件:code

    • 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:可切换线程。

 

 

 

(未完待续...)

相关文章
相关标签/搜索