Reactive Cocoa Tutorial [2] = "百变RACStream";
2014-02-14 00:03
531 查看
Overview
在RAC下开发干的最多的事就是建立RACSignal和subscribe RACSignal了,它是RAC的核心所在。本篇介绍了RAC的运作原理和设计思路,从函数式编程形成的RACStream继而介绍它的子类 - RAC最核心的部分RACSignal。· 函数式编程
我们知道Reactive Cocoa是函数式编程(Functional Programing)(FP)思想的实现。FP有一套成熟的理论,这里只讲讲我个人理解吧。我觉得FP就是“像计算函数表达式一样来解决一个问题”,举个栗子,中学题:
当然,仔细看这个函数,其实是可以分解成几个小函数的:
再来看一个函数是怎么构成的,FP理论里叫monads,十分抽象,没读懂,但能理解出来:一个函数只要有一个对于输入值的运算方法和一个返回值,就够了。也容易理解,给它一个输入,干点事情,给出一个输出,就行了,当然现实情况要复杂得多(比如说输出值本身就是个函数?)有些函数是有输入的条件的,比如原来数学解个函数时候经常跟个作用域或者限制条件,比如f(x)
= 10 / x , (x不为0),要是传个0这个函数就认为计算错误。
对于像上面栗子的函数,每个函数都能接收上一个函数输出的结果,作为自己的输入,这样才能嵌套生成最终结果,同时,计算的顺序也是一定从里向外,所以换个写法可以写成:
·RACStream
这就是RACStream所表示的含义。按照上面说的,其实RACStream的名字有点点歧义,对于一个RACStream对象,它在意义上等同于上面的f1(x),f2(x),f3(x),而不是那一大串整体,表示整体的应该是最外层的和f(x)对应的那个对象,叫个RACStreamComponent比较好?理解时候得注意下。
所以作为一个基本函数的RACStream应该至少应该有:
怎么传入值
怎么返回值
怎么与其他函数组合
怎么实现函数的作用域(监测输入值来做处理)
这函数叫啥- -
得益于在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连接起来的基本方法了:
[A concat:B]中A和B像皇上和太子的关系,A是皇上,B是太子。皇上健在的时候统治天下发号施令(value),太子就候着,不发号施令(value),当皇上挂了(completed),太子登基当皇上,此时发出的号令(value)是太子的。
[C zipWith:D]可以比喻成一对平等恩爱的夫妻,两个人是“绑在一起“的关系来组成一个家庭,决定一件事(value)时必须两个人都提出意见(当且仅当C和D同时都产生了值的时候,一个value才被输出,CD只有其中一个有值时会挂起等待另一个的值,所以输出都是一对值(RACTuple)),当夫妻只要一个人先挂了(completed)这个家庭(组合起来的RACStream)就宣布解散(也就是无法凑成一对输出时就终止)
相关文章推荐
- Reactive Cocoa Tutorial [2] = "百变RACStream";
- Reactive Cocoa Tutorial [3] = "RACSignal的巧克力工厂“;
- Reactive Cocoa Tutorial [1] = "神奇的Macros";
- iOS ReactiveCocoa(RAC)学习详解
- MVVM设计模式教程 - tutorial with ReactiveCocoa
- ReactiveCocoa之RACMulticastConnection使用(六)
- iOS开发之ReactiveCocoa框架(RAC)第五篇队列与高级函数
- iOS开发之ReactiveCocoa框架(RAC)第六篇程序定位
- ReactiveCocoa实战: 模仿 "花瓣",重写 LeanCloud Rest Api的iOS REST Client.
- ReactiveCocoa之RAC过滤(八)
- iOS Reactivecocoa(RAC)知其所以然(源码分析,一篇足以)
- ReactiveCocoa Tutorial – The Definitive Introduction: Part 1/2
- ReactiveCocoa 中 RACSignal 是怎样发送信号
- ReactiveCocoa之RAC合并(九)
- ReactiveCocoa Tutorial – The Definitive Introduction
- iOS Reactivecocoa(RAC)知其所以然(源码分析,一篇足以)
- ReactiveCocoa之RAC常用宏(十)
- Reactive Cocoa Tutorial [4] = 只取所需的Filters
- ReactiveCocoa Tutorial – The Definitive Introduction: Part 1/2
- Reactive Cocoa Tutorial