ReactiveCocoa 是 GitHub 开源的一款 Cocoa FRP(函数响应式编程)框架。在之前的版本介绍中,我们接触过 1.x 版本,而 2.x 版本带来了不少新变化,API 也有部分不兼容的地方。这里再系统地梳理一下。
Native App 开发中,很大一部分时间其实是在等待事件发生然后响应。比如等待网络请求完成、等待用户操作、或者某些状态值的改变。等这些事件发生后,再做进一步处理。但是这些等待和响应,并没有一个统一的处理方式。Delegate、Notification、Block、KVO 常常让人不知道该用哪个最合适。有时需要 chain 或 compose 某几个事件,就需要多个状态变量,而状态变量一多,复杂度也就上来了。为了解决这些问题,GitHub 的工程师们开发了 ReactiveCocoa。
几个常见的概念
在阅读 ReactiveCocoa(以下简称 RAC)的相关文章或代码时,经常会出现一些名词,理解它们对于理解 RAC 有很大的帮助。
Signal 和 Subscriber
这是 RAC 最核心的内容。这里我想用插头和插座来描述:插座是 Signal,插头是 Subscriber。想象某个遥远的星球,他们的电像某种物质一样被集中存储,且很珍贵。插座负责去获取电,插头负责使用电,而且一个插座可以插任意数量的插头。当一个插座(Signal)没有插头(Subscriber)时什么也不干,也就是处于冷(Cold)的状态,只有插了插头时才会去获取,这个时候就处于热(Hot)的状态。
Signal 获取到数据后,会调用 Subscriber 的 sendNext、sendComplete、sendError 方法来传送数据给 Subscriber,Subscriber 自然也有方法来获取传过来的数据,如:[signal subscribeNext:error:completed]。这样只要没有 sendComplete 和 sendError,新的值就会通过 sendNext 源源不断地传送过来。
举个简单的例子:
[RACObserve(self, username) subscribeNext:^(NSString *newName){
NSLog(@"newName:%@", newName);
}];
RACObserve 使用了 KVO 来监听 property 的变化,只要 username 被自己或外部改变,block 就会被执行。但不是所有的 property 都可以被 RACObserve,该 property 必须支持 KVO,比如 NSURLCache 的 currentDiskUsage 就不能被 RACObserve。
Signal 是很灵活的,它可以被修改(map),过滤(filter),叠加(combine),串联(chain),这有助于应对更加复杂的情况。比如:
RAC(self.logInButton, enabled) = [RACSignal \
combineLatest:@[self.usernameTextField.rac_textSignal,
self.passwordTextField.rac_textSignal,
RACObserve(LoginManager.sharedManager, loggingIn),
RACObserve(self, loggedIn)] reduce:^(NSString *username, NSString *password, NSNumber *loggingIn, NSNumber *loggedIn) {
return @(username.length > 0 && password.length > 0 && !loggingIn.boolValue && !loggedIn.boolValue);
}];
这段代码看起来有点复杂,来细细说一下。首先是左边的 RAC(...),它的作用是将 self.logInButton.enabled 属性与右边的 signal 的 sendNext 值绑定。也就是如果右边的 reduce 的返回值为 NO,那么 enabled 就为 NO。右边的 combineLatest 是获取这 4 个 signal 的 next 值。其中可以看到 self.usernameTextField.rac_textSignal 这么个东东,rac_textSignal 是 RAC 为 UITextField 添加的 category,只要 usernameTextField 的值有变化,这个值就会被返回()。 需要每个 signal 至少都有过一次 。 的作用是根据接收到的值,再返回一个新的值,这里是 和 ,必须是 object。


