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是Github的一个开源的项目,是IOS平台上对FRP的实现。
RACStream就对应FRP中的事件流,一个RACStream对象的意义相当于上面的f1(x)(每一个基本函数都有一个与之对应的RACStream对象),最后组合出来的函数f(x)则由RACStreamComponent表示。一个RACStream对象应有几个基本要素:
信号,ReactiveCocoa中的核心,是一个主动信号流,它表示未来将要发送的数据,也可以将它当做一个“事件转发器”。一个signal和一个数据源绑定,当数据源的数据有更新时,signal会向signal所有的subscriber(订阅者)发送事件。
Signal只会向subscriber发送三种类型的事件:
一个Signal的生命周期内可以接受任意多个Next事件,一个Error事件或者是Completed事件(两种事件只可能出现一种)。
最有意思的是,Signals之间能够进行各种组合,具体说来,它可以被修改(Map)、过滤(Filter)、叠加(Combine)和串联(Chain)。
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时,才会被发到下一个接收方继续执行。
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。
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是ReactiveCocoa中对线程的简单封装,事件可以在指定的scheduler上分发执行,默认情况是事件都在一个默认的后台线程里面执行,如遇特殊情况需要在主线程调用,使用 deliverOn:可切换线程。
(未完待续...)