ReactiveCocoa 打破了苹果 API 排他性的束缚,勇敢地开创了 Objective-C 的新纪元,具有划时代的意义。
2.5版本的目录结构
-
信号源
RACStream
RACSignal
RACSubject
RACSequence
-
订阅者
RACSubscriber
RACMulticastConnection
-
调度器
RACScheduler
-
清洁工
RACDisposable
对于一个应用来说,绝大部分的时间都是在等待某些事件的发生或响应某些状态的变化,比如用户的触摸事件、应用进入后台、网络请求成功刷新界面等等,而维护这些状态的变化,常常会使代码变得非常复杂,难以扩展。而 ReactiveCocoa 给出了一种非常好的解决方案,它使用信号来代表这些异步事件,提供了一种统一的方式来处理所有异步的行为,包括代理方法、block 回调、target-action 机制、通知、KVO 等。RAC将这其中的各种输入,都抽象成了信号(也可以理解为状态流)让单一的组件能够对自己的响应动作进行控制,简化了视图控制器的负担。
// 代理方法
[[self rac_signalForSelector:@selector(webViewDidStartLoad:)
fromProtocol:@protocol(UIWebViewDelegate)]
subscribeNext:^(id x) {
// 实现 webViewDidStartLoad: 代理方法
}];
// target-action
[[self.avatarButton
rac_signalForControlEvents:UIControlEventTouchUpInside]
subscribeNext:^(UIButton *avatarButton) {
// avatarButton 被点击了
}];
// 通知
[[[NSNotificationCenter defaultCenter]
rac_addObserverForName:kReachabilityChangedNotification object:nil]
subscribeNext:^(NSNotification *notification) {
// 收到 kReachabilityChangedNotification 通知
}];
// KVO
[RACObserve(self, username) subscribeNext:^(NSString *username) {
// 用户名发生了变化
}];
然而,这些还只是 ReactiveCocoa 的冰山一角,它真正强大的地方在于我们可以对这些不同的信号进行任意地组合和链式操作,从最原始的输入 input 开始直至得到最终的输出 output 为止:
[[[RACSignal
combineLatest:@[ RACObserve(self, username), RACObserve(self, password) ]
reduce:^(NSString *username, NSString *password) {
return @(username.length > 0 && password.length > 0);
}]
distinctUntilChanged]
subscribeNext:^(NSNumber *valid) {
if (valid.boolValue) {
// 用户名和密码合法,登录按钮可用
} else {
// 用户名或密码不合法,登录按钮不可用
}
}];
信号源
Streams of values over time.
你可以把它想象成水龙头中的水,当你打开水龙头时,水源源不断地流出来;你也可以把它想象成电,当你插上插头时,电静静地充到你的手机上;你还可以把它想象成运送玻璃珠的管道,当你打开阀门时,珠子一个接一个地到达。这里的水、电、玻璃珠就是我们所需要的值,而打开水龙头、插上插头、打开阀门就是订阅它们的过程。
RACSignal
使用举例:
RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) {
[subscriber sendNext:@1];
[subscriber sendNext:@2];
[subscriber sendCompleted];
return [RACDisposable disposableWithBlock:^{
NSLog(@"dispose");
}];
}];
[signal subscribeNext:^(id _Nullable x) {
NSLog(@"%@", x);
}];
控制台输出
1
2
dispose
可以将RACSignal 理解为一连串的状态,在状态改变时,对应的订阅者 RACSubscriber 就会收到通知执行相应的指令,在 ReactiveCocoa 的世界中所有的消息都是通过信号的方式来传递的,原有的设计模式都会简化为一种模型。
状态
1.类簇
RACSignal 在创建实例时可能会返回
RACDynamicSignal、RACEmptySignal、RACErrorSignal 和RACReturnSignal 对象。
+createSignal:->RACDynamicSignal
+error:->RACErrorSignal
+empty->RACEmptySignal
+return->RACReturnSignal
RACStream
抽象方法和operation分类中的方法上面的这些抽象方法都需要子类覆写,但RACStream 在 Operations 分类中使用上面的抽象方法提供了丰富的内容,比如上面的 -flattenMap: 方法:
- (__kindof RACStream *)flattenMap:(__kindof RACStream * (^)(id value))block {
Class class = self.class;
return [[self bind:^{
return ^(id value, BOOL *stop) {
id stream = block(value) ?: [class empty];
NSCAssert([stream isKindOfClass:RACStream.class], @"Value returned from -flattenMap: is not a stream: %@", stream);
return stream;
};
}] setNameWithFormat:@"[%@] -flattenMap:", self.name];
}
订阅者
RAC中信号的订阅与信息的发送过程主要是由RACSubscriber类来处理的。在信号创建之后调用 -subscribeNext: 方法返回一个 RACDisposable,然而这不是这一流程关心的重点,在订阅过程中生成了一个 RACSubscriber 对象,向这个对象发送消息 -sendNext: 时,就会向所有的订阅者发送消息。
image.png
信号的订阅
信号的订阅与subscribe开头的一系列方法有关。
订阅者可以选择自己想要感兴趣的信息类型 next/error/completed 进行关注,并在对应的信息发生时调用 block 进行处理回调。
所有的方法其实只是对 nextBlock、completedBlock 以及 errorBlock 的组合,这里以其中最长的 -subscribeNext:error:completed: 方法的实现为例(也只需要介绍这一个方法):
- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
RACSubscriber *o = [RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
return [self subscribe:o];
}
其中:[RACSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed {
RACSubscriber *subscriber = [[self alloc] init];
subscriber->_next = [next copy];
subscriber->_error = [error copy];
subscriber->_completed = [completed copy];
return subscriber;
}
其中: [self subscribe:o];
- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
RACCompoundDisposable *disposable = [RACCompoundDisposable compoundDisposable];
subscriber = [[RACPassthroughSubscriber alloc] initWithSubscriber:subscriber signal:self disposable:disposable];
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{
RACDisposable *innerDisposable = self.didSubscribe(subscriber);
[disposable addDisposable:innerDisposable]; }];
[disposable addDisposable:schedulingDisposable];
return disposable;
}
网友评论