Reactive Cocoa Tutorial [2] = "百变RACStream";

Reactive Cocoa Tutorial 系列,转载请注明该文源地址 -- by sunnyhtml


  · Overview

  在RAC下开发干的最多的事就是创建RACSignal和subscribe RACSignal了,它是RAC的核心所在。本篇介绍了RAC的运做原理和设计思路,从函数式编程造成的RACStream继而介绍它的子类 - RAC最核心的部分RACSignal。编程

· 函数式编程

  咱们知道Reactive Cocoa是函数式编程(Functional Programing)(FP)思想的实现。FP有一套成熟的理论,这里只讲讲我我的理解吧。数组

  我以为FP就是“像计算函数表达式同样来解决一个问题”,举个栗子,中学题:函数式编程

已知:f(x) = 2sin(x + π/2), 求 f(π/2)的值。

  其中x是这个函数的输入,f(x)为计算的输出结果,求f(π/2)时给定了x天然能计算出个结果来(说实话我真忘了咋算了)函数

固然,仔细看这个函数,实际上是能够分解成几个小函数的:spa

f1(x) = x + π/2
f2(x) = sin(x)
f3(x) = 2x

  而原来的f(x)能够被小函数组合:设计

f(x) = f3(f2(f1(x)))

  因此不可贵出这么个推论:要是我手上有足够的基本函数,我就能用上面的组合的方法组合出任意一个复杂的函数了。再想一想事实上这些年来学数学的过程不就是在一个个积累基本函数的过程嘛,从基本运算,到三角函数,到乘方开方,再到微积分。基本函数愈来愈多,能解决的数学问题也愈来愈复杂。code

  再来看一个函数是怎么构成的,FP理论里叫monads,十分抽象,没读懂,但能理解出来:一个函数只要有一个对于输入值的运算方法一个返回值,就够了。也容易理解,给它一个输入,干点事情,给出一个输出,就好了,固然现实状况要复杂得多(好比说输出值自己就是个函数?)有些函数是有输入的条件的,好比原来数学解个函数时候常常跟个做用域或者限制条件,好比f(x) = 10 / x , (x不为0),要是传个0这个函数就认为计算错误。htm

  对于像上面栗子的函数,每一个函数都能接收上一个函数输出的结果,做为本身的输入,这样才能嵌套生成最终结果,同时,计算的顺序也是必定从里向外,因此换个写法能够写成:对象

start ---x--> f1(x) --(temp value1)--> f2(temp value1) --(temp value2)--> f3(temp value2) ---> result

  因而乎嵌套就被表示成了序列,来个高大上的名字怎么样,就叫(Stream)

·RACStream

  这就是RACStream所表示的含义。

  按照上面说的,其实RACStream的名字有点点歧义,对于一个RACStream对象,它在乎义上等同于上面的f1(x),f2(x),f3(x),而不是那一大串总体,表示总体的应该是最外层的和f(x)对应的那个对象,叫个RACStreamComponent比较好?理解时候得注意下。

  因此做为一个基本函数的RACStream应该至少应该有:

    1. 怎么传入值
    2. 怎么返回值
    3. 怎么与其余函数组合
    4. 怎么实现函数的做用域(监测输入值来作处理)
    5. 这函数叫啥- -

  得益于在Objc下实现,因此输入输出的“值”都用个id类型就好了,遇到多个值的组合就用RACTurple(能够把多个值压包和解包,类比WINRAR),1和2解决

  RACStream从实例变量来看只有一个name,固然它也只应该有个name - -,5解决

  里面重点问题就是上面的3和4了。因为函数组合以后仍然是个函数,因此也很容易理解两个Stream对象的组合其实就是生成一个新的Stream对象,它返回了分别由两个子Stream前后运算产生的最终结果

   观摩一下RACStream定义的基本方法:

+ (instancetype)empty;
+ (instancetype)return:(id)value;
- (instancetype)bind:(RACStreamBindBlock (^)(void))block; // for 4
- (instancetype)concat:(RACStream *)stream; // for 3
- (instancetype)zipWith:(RACStream *)stream; // for 3

  RACStream做为一个描述抽象的父类,这几个基本方法并无实现,是由具体子类来实现,RACStream的两个子类分别是RACSignal和RACSequence

  +empty 是一个不返回值,马上结束(Completed)的函数,意思是执行它以后除了马上结束啥都不会发生,能够理解为RAC里面的nil。

  +return: 是一个直接返回给定值,而后马上结束的函数,好比 f(x) = 213

  -bind:是一个很是重要的函数,在Rac Doc中被描述为‘basic primitives, particularly’,它是RACStream监测“值”和控制“运行状态”的基本方法,我的认为看注释文档不能理解它是干吗的,并且bind英语“捆绑,绑定,强迫,约束”这几个意思也感受对不上,我以为叫“绑架”却是更贴切一点。在-bind:以后,以前的RACStream就处于被“绑架”的状态,被绑架的RACStream每产生一个值,都要通过“绑架者”来决定:

1. 是否使这个RACStream结束(被绑架者是否还能继续活着)

2. 用什么新的RACStream来替换被绑架的RACStream,传出的结果也成了新RACStream产生的值(绑匪能够选择再抓一我的质放以前那个前面)

   举个具体栗子,RACStream的 - take:方法,这个方法使一个RACStream只取前N次的值(有缩减):

- (instancetype)take:(NSUInteger)count {
    Class class = self.class;
    
    return [[self bind:^{ // self被绑架
        __block NSUInteger taken = 0;

        return ^ id (id value, BOOL *stop) { // 这个block在被绑架的self每输出一个值得时候触发
            RACStream *result = class.empty;

            if (taken < count) result = [class return:value]; // 未达到N次时将原值原本来本的传递出去
            if (++taken >= count) *stop = YES; // 达到第N次值后干掉了被绑架的self

            return result; // 将被绑架的self替换为result
        };
    }]];
}

   -concat: 和 -zipWith: 就是将两个RACStream链接起来的基本方法了:

  1. [A concat:B]中A和B像皇上和太子的关系,A是皇上,B是太子。皇上健在的时候统治天下发号施令(value),太子就候着,不发号施令(value),当皇上挂了(completed),太子登基当皇上,此时发出的号令(value)是太子的。
  2. [C zipWith:D]能够比喻成一对平等恩爱的夫妻,两我的是“绑在一块儿“的关系来组成一个家庭,决定一件事(value)时必须两我的都提出意见(当且仅当C和D同时都产生了值的时候,一个value才被输出,CD只有其中一个有值时会挂起等待另外一个的值,因此输出都是一对值(RACTuple)),当夫妻只要一我的先挂了(completed)这个家庭(组合起来的RACStream)就宣布解散(也就是没法凑成一对输出时就终止)

· 而后呢?

  除了上面几个基本方法,RACStream还有很多的Operation方法,这些操做方法的实现大都是组合基本的方法来达到特定的目的,虽然是RACStream这个基类实现的,但我以为仍是放在后面介绍RACSignal的时候做为它的使用方法来讲比较合适,毕竟绝大多数编程的对象的都是RACStream的两个子类,后面再展开介绍好了。

相关文章
相关标签/搜索