ReactiveCocoa (RAC) 是 iOS/macOS 开发中一个强大的函数响应式编程框架,它通过信号(Signals)和序列(Sequences)的概念来处理异步事件流。下面我将深入解析 RAC 的实现机制和消息传递策略。
一、RAC 核心实现机制
1. 信号(Signal)的底层实现
RAC 的核心是 **RACSignal**
类,其基本结构如下:
@interface RACSignal : NSObject
+ (RACSignal *)createSignal:(RACDisposable * (^)(id<RACSubscriber> subscriber))didSubscribe;
- (RACDisposable *)subscribeNext:(void (^)(id value))nextBlock
error:(void (^)(NSError *error))errorBlock
completed:(void (^)(void))completedBlock;
@end
实现原理:
- 每个信号都是一个事件流(event stream)
- 信号被订阅时执行
**didSubscribe**
块 - 订阅者(subscriber)接收三种事件:
**next**
: 携带值的事件**error**
: 错误事件(终止信号)**completed**
: 完成事件(终止信号)
2. 订阅机制
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
// 1. 创建订阅者
RACSubscriber *s = [RACSubscriber subscriberWithNext:^(id x) {
[subscriber sendNext:x];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
// 2. 执行创建信号时的 block
RACDisposable *creatorDisposable = self.didSubscribe(s);
// 3. 管理订阅生命周期
[disposable addDisposable:creatorDisposable];
[disposable addDisposable:[RACDisposable disposableWithBlock:^{
// 清理资源
}]];
return disposable;
}
3. 操作符实现原理
RAC 的操作符(如 map, filter, combineLatest 等)都是通过创建新信号实现的:
- (RACSignal *)map:(id (^)(id value))block {
return [RACSignal createSignal:^(id<RACSubscriber> subscriber) {
return [self subscribeNext:^(id x) {
id mappedValue = block(x);
[subscriber sendNext:mappedValue];
} error:^(NSError *error) {
[subscriber sendError:error];
} completed:^{
[subscriber sendCompleted];
}];
}];
}
二、RAC 消息传递策略
RAC 提供了多种消息传递策略,适用于不同场景:
1. 基本订阅 (Subscription)
策略特点:
- 点对点直接传递
- 信号每次发送值都会通知订阅者
- 支持错误和完成事件
RACSignal *signal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendCompleted];
return nil;
}];
[signal subscribeNext:^(id x) {
NSLog(@"Received: %@", x);
}];
2. 命令模式 (RACCommand)
策略特点:
- 封装可执行操作
- 自动管理执行状态
- 防止重复执行
- 返回执行结果信号
RACCommand *command = [[RACCommand alloc] initWithSignalBlock:^RACSignal *(id input) {
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
// 执行网络请求等操作
[subscriber sendNext:@"Result"];
[subscriber sendCompleted];
return nil;
}];
}];
// 执行命令
[command execute:@"input"];
// 监听执行结果
[command.executionSignals subscribeNext:^(RACSignal *signal) {
[signal subscribeNext:^(id result) {
NSLog(@"Result: %@", result);
}];
}];
3. 通道绑定 (RACChannel)
策略特点:
- 实现双向数据绑定
- 保持两个属性同步更新
- 避免循环更新
// ViewModel
RACChannelTerminal *integerTerminal = RACChannelTo(self, integerValue);
// View
RACChannelTerminal *sliderTerminal = [slider rac_newValueChannelWithNilValue:@0];
// 双向绑定
[integerTerminal subscribe:sliderTerminal];
[sliderTerminal subscribe:integerTerminal];
4. 多播连接 (RACMulticastConnection)
策略特点:
- 共享信号执行结果
- 避免多次执行副作用
- 冷信号转热信号
RACSignal *signal = [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"执行网络请求");
[subscriber sendNext:@"Data"];
return nil;
}] replay]; // 使用replay创建多播
// 多个订阅者共享同一个执行
[signal subscribeNext:^(id x) { /* ... */ }];
[signal subscribeNext:^(id x) { /* ... */ }];
5. 代理转发 (RACDelegateProxy)
策略特点:
- 将传统代理转为信号
- 统一处理代理方法
- 避免代理方法分散
// 创建代理代理
RACDelegateProxy *proxy = [[RACDelegateProxy alloc] initWithProtocol:@protocol(UITableViewDelegate)];
// 将代理方法转为信号
[[proxy rac_signalForSelector:@selector(tableView:didSelectRowAtIndexPath:)]
subscribeNext:^(RACTuple *arguments) {
UITableView *tableView = arguments.first;
NSIndexPath *indexPath = arguments.second;
// 处理选择事件
}];
// 设置代理
tableView.delegate = (id<UITableViewDelegate>)proxy;
6. 通知中心 (RACNotification)
策略特点:
- 将通知转为信号
- 自动管理生命周期
- 与RAC管道无缝集成
[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:UIKeyboardWillShowNotification object:nil]
subscribeNext:^(NSNotification *notification) {
// 处理键盘显示
}];
7. KVO 监听 (RACKVO)
策略特点:
- 将KVO转为信号
- 自动处理内存管理
- 支持键路径
[RACObserve(self, username)
subscribeNext:^(NSString *newName) {
NSLog(@"用户名变为: %@", newName);
}];
三、RAC 消息传递的核心特性
1. 惰性求值 (Lazy Evaluation)
信号在被订阅前不会执行:
RACSignal *lazySignal = [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
NSLog(@"这行只会在订阅时打印");
return nil;
}];
// 此时不会打印
// ...
// 订阅后才会执行
[lazySignal subscribeCompleted:^{}];
2. 链式操作 (Chaining Operations)
操作符可以链式组合:
[[[[RACSignal interval:1.0 onScheduler:[RACScheduler mainThreadScheduler]]
take:5] // 只取5次
filter:^BOOL(NSDate *date) {
return date.timeIntervalSince1970 > someTime; // 过滤
}]
map:^id(NSDate *date) {
return [date description]; // 转换
}];
3. 错误传播 (Error Propagation)
错误会自动向下游传播:
[[signal
flattenMap:^RACSignal *(id value) {
return [RACSignal error:[NSError errorWithDomain:@"..." code:0 userInfo:nil]];
}]
subscribeError:^(NSError *error) {
// 这里会捕获错误
}];
4. 资源管理 (Resource Management)
通过 **RACDisposable**
管理资源:
RACDisposable *disposable = [signal subscribeNext:^(id x) {
// ...
}];
// 当不再需要时取消订阅
[disposable dispose];
四、高级消息传递模式
1. 背压处理 (Backpressure)
处理生产者-消费者速度不匹配:
[[signal
bufferWithTime:0.5 onScheduler:[RACScheduler scheduler]] // 缓冲500ms
subscribeNext:^(RACTuple *values) {
// 批量处理值
}];
2. 重试机制 (Retry)
自动重试失败的操作:
[[[networkRequestSignal
retry:3] // 最多重试3次
catch:^RACSignal *(NSError *error) {
// 重试失败后处理
return [RACSignal return:defaultValue];
}]
subscribeNext:^(id x) {
// ...
}];
3. 节流防抖 (Throttle/Debounce)
控制事件频率:
// 节流:每0.3秒最多发送一个值
[[searchField.rac_textSignal
throttle:0.3]
subscribeNext:^(NSString *text) {
// 执行搜索
}];
// 防抖:停止输入0.5秒后发送
[[searchField.rac_textSignal
debounce:0.5]
subscribeNext:^(NSString *text) {
// 执行搜索
}];
五、RAC 消息传递的最佳实践
- 合理使用热信号与冷信号:
- 冷信号:每次订阅都会重新执行
- 热信号:多个订阅者共享执行结果
- 避免循环引用:
@weakify(self);
[signal subscribeNext:^(id x) {
@strongify(self);
[self doSomething];
}];
- 合理使用调度器:
[[signal
deliverOn:[RACScheduler scheduler]] // 在后台执行
subscribeOn:[RACScheduler mainThreadScheduler] // 结果回到主线程
subscribeNext:^(id x) {
// 在主线程更新UI
}];
- 组合信号处理复杂逻辑:
RACSignal *combined = [RACSignal combineLatest:@[signalA, signalB] reduce:^id(id a, id b){
return @([a boolValue] && [b boolValue]);
}];
- 使用RAC宏简化代码:
// 双向绑定
RACChannelTo(viewModel, property) = RACChannelTo(view, property);
// 自动绑定
RAC(self.outputLabel, text) = RACObserve(self.viewModel, outputText);
六、性能优化策略
- 避免不必要的订阅:
// 使用 takeUntil 自动取消订阅
[signal takeUntil:self.rac_willDeallocSignal];
- 合理使用 replay 和 replayLast:
// 只重放最后的值
RACSignal *replayed = [signal replayLast];
- 减少不必要的线程切换:
// 避免不必要的线程切换
[[signal
filter:^BOOL(id value) {
// 在后台线程过滤
return ...;
}]
deliverOnMainThread]; // 最后才切换到主线程
- 使用 RACSequence 处理集合:
NSArray *results = [[array.rac_sequence
filter:^BOOL(id value) {
return ...;
}]
map:^id(id value) {
return ...;
}].array;
ReactiveCocoa 提供了一套强大的消息传递机制,通过信号和操作符的组合,可以优雅地处理各种异步事件流。掌握其核心实现机制和多种消息传递策略,能够显著提高代码的可读性、可维护性和响应性。