ReactiveCocoa 在设计上很大程度借鉴了 Reactive Extension 中的概念,可以说 ReactiveCocoa 是 Rx 在 Objective-C 语言中的实现
Rx 中主要的两个概念信号和序列都在 ReactiveCocoa 中有着相对应的组件 RACSignal
和 RACSequence
RACSignal 和 RACSequence
最大区别就是:
RACSignal 是推驱动的,也就是在每次信号中的出现新的数据时,所有的订阅者都会自动接受到最新的值;
RACSequence 作为拉驱动的数据流,在改变时并不会通知使用当前序列的对象,只有使用者再次从这个 RACSequence 对象中获取数据才能更新,它的更新是需要使用者自己拉取的。
eg:
1 2 3 4 5 6 7 8 9 10 11 12
| -(void)setupRAC { [self.networkingRAC.executionSignal subscribeNext:^(id _Nullable x) { NSArray<WSFOrderAddressRegionModel*>* origionModelAry=[self.adrRegionAPi fetchDataFromModel:WSFOrderAddressRegionModel.class]; RACSequence *origionModelSeq = [origionModelAry.rac_sequence map:^id(WSFOrderAddressRegionModel *model) { return [[YWAddressModel alloc] initWithModel:model]; }]; self.orderAddrAry=origionModelSeq.array; }]; }
|
RACSignal 和 RACSequence转换
1 2 3 4 5
| RACSequence *sequence = @[@1, @2, @3].rac_sequence; RACSignal *signal = sequence.signal; [signal subscribeNext:^(id _Nullable x) { NSLog(@"%@", x); }];
|
1 2 3 4 5 6 7 8
| RACSignal *signal = [RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { [subscriber sendNext:@1]; [subscriber sendNext:@2]; [subscriber sendNext:@3]; [subscriber sendCompleted]; return nil; }]; NSLog(@"%@", signal.toArray.rac_sequence);
|
热信号 RACSubject
RACSubject 是 RACSignal 的子类,RACSubject 在 RACSignal 对象之上进行了简单的修改,将原有的冷信号改造成了热信号,将不可变变成了可变。
1
| RACSubject 存储了一个遵循 RACSubscriber 协议的对象列表以及所有的消息,
|
1 2 3 4 5
| @interface RACSubject : RACSignal <RACSubscriber>
+ (instancetype)subject;
@end
|
它与 RACSignal 最大的不同就是:RACSubject 实现了 RACSubscriber 协议
1 2 3 4 5 6 7 8 9
| @protocol RACSubscriber <NSObject> @required
- (void)sendNext:(nullable id)value; - (void)sendError:(nullable NSError *)error; - (void)sendCompleted; - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable;
@end
|
冷热信号
冷信号是被动的,只会在被订阅时向订阅者发送通知;热信号是主动的,它会在任意时间发出通知,与订阅者的订阅时间无关。
也就是说冷信号所有的订阅者会在订阅时收到完全相同的序列;而订阅热信号之后,只会收到在订阅之后发出的序列。
热信号的订阅者能否收到消息取决于订阅的时间。
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
-(void)setupRAC{ @weakify(self) [[RACObserve(self, cellModel) skip:1] subscribeNext:^(WSFOrderLogisticsCompanyLastestModel* _Nullable model) { @strongify(self); if (model.isSelected) { self->itemBtn.selected=YES; } else { self->itemBtn.selected=NO; } [ self->itemBtn setTitle:model.shipperName forState:UIControlStateNormal]; }]; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
|
if (kAppDelegate.appHadEnterBackground) { [self placeOrderWithAccountModel:acctountModel ServiceType:serviceType]; } else { __block RACDisposable* disposable= [WSFUSERManger.userPermissionUpdatedSub subscribeNext:^(id _Nullable x) { [self placeOrderWithAccountModel:acctountModel ServiceType:serviceType]; [disposable dispose]; }]; } @property(nonatomic)RACSubject* userPermissionUpdatedSub; [[self.networkingRACs[UserPermissionApiIdentifier] executionSignal] subscribeNext:^(id x) { @strongify(self); NSMutableDictionary *tempDic = [NSMutableDictionary dictionary]; NSArray *permissionModelArys = [self.userPermissionApi fetchDataFromModel:WSFMenuPermissionModel.class]; [permissionModelArys enumerateObjectsUsingBlock:^(WSFMenuPermissionModel *obj, NSUInteger idx, BOOL * _Nonnull stop) { if ([obj.permission isEqualToString:@"allow"]) { obj.isPermissionAllow = true; } else if ([obj.permission isEqualToString:@"deny"]) { obj.isPermissionAllow = false; } tempDic[obj.nameEN] = obj; }]; self.menuPermissionModelDics = tempDic; [self.userPermissionUpdatedSub sendNext:nil]; }];
|
RACBehaviorSubject 与 RACReplaySubject
RACBehaviorSubject 在订阅时会向订阅者发送最新的消息,
RACReplaySubject 在订阅之后可以重新发送之前的所有消息序列。
RACBehaviorSubject
RACBehaviorSubject,它在内部会保存一个 currentValue 对象,也就是最后一次发送的消息:
1 2 3 4 5 6 7 8 9 10 11 12
| @interface RACBehaviorSubject ()
@property (nonatomic, strong) id currentValue;
@end
- (void)sendNext:(id)value { @synchronized (self) { self.currentValue = value; [super sendNext:value]; } }
|
向最新的订阅者发送之前的消息,这是通过覆写 -subscribe: 方法实现的
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber { RACDisposable *subscriptionDisposable = [super subscribe:subscriber];
RACDisposable *schedulingDisposable = [RACScheduler.subscriptionScheduler schedule:^{ @synchronized (self) { [subscriber sendNext:self.currentValue]; } }];
return [RACDisposable disposableWithBlock:^{ [subscriptionDisposable dispose]; [schedulingDisposable dispose]; }]; }
|
RACReplaySubject
RACReplaySubject 相当于一个自带 buffer 的 RACBehaviorSubject,它可以在每次有新的订阅者订阅之后发送之前的全部消息。
1 2 3 4 5 6
| @interface RACReplaySubject ()
@property (nonatomic, assign, readonly) NSUInteger capacity; @property (nonatomic, strong, readonly) NSMutableArray *valuesReceived;
@end
|
RACCommand
RACCommand 并不表示数据流,它只是一个继承自 NSObject 的类,是一个用于管理 RACSignal 的创建与订阅的类
RACCommand 的初始化与执行
1 2 3 4
| - (instancetype)initWithSignalBlock:(RACSignal<ValueType> * (^)(InputType _Nullable input))signalBlock;
- (RACSignal<ValueType> *)execute:(nullable InputType)input;
|
在默认情况下 RACCommand 都是不支持并发操作的,需要在上一次命令执行之后才可以发送下一次操作,否则就会返回错误信号 RACErrorSignal,这些错误可以通过订阅 command.errors 获得
RACCommand 接口中的高阶信号
它在接口中暴露出的四个信号
executionSignals
在每次执行 -execute: 方法时,最终都会向 executionSignals 中传入一个最新的信号
executing
executing 是一个表示当前是否有任务执行的信号
enabled
enabled 信号流表示当前的命令是否可以再次被执行,也就是 -execute: 方法能否可以成功执行新的任务;
- errors
为了保证 RACCommand 对此执行 -execute: 方法也可以继续运行,我们只能将所有的错误以其它的形式发送到 errors 信号中,防止向 executionSignals 发送错误信号后,executionSignals 信号就会中止的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13
| RACMulticastConnection *errorsConnection = [[[self.addedExecutionSignalsSubject flattenMap:^(RACSignal *signal) { return [[signal ignoreValues] catch:^(NSError *error) { return [RACSignal return:error]; }]; }] deliverOn:RACScheduler.mainThreadScheduler] publish];
_errors = [errorsConnection.signal setNameWithFormat:@"%@ -errors", self]; [errorsConnection connect];
|
RACChannel双向绑定
一对一的双向数据流 RACChannel,RACChannel 可以被理解为一个双向的连接,这个连接的两端都是 RACSignal 实例,它们可以向彼此发送消息
RACChannel 内部封装了两个 RACChannelTerminal 对象,它们都是 RACSignal 的子类:
eg:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| WSFPlaceOrderParamModel* modelName=[[WSFPlaceOrderParamModel alloc] init]; modelName.showTipText=false; modelName.tilteParam=@"姓名"; modelName.valueTipParam=@"请输入姓名"; modelName.valuePlaceHolder=@"请输入姓名"; modelName.valuePlaceParam=nil; modelName.modelType=WSFPlaceOrderCellTypeInput; modelName.modelId=WSFPlaceOrderModelIdAddrName; RACChannelTo(modelName, valuePlaceParam)=RACChannelTo(WSFOrderManager,orderExtraData.buyerName); -(void)setupRAC { @weakify(self); [[self->_valueTxt.rac_textSignal filter:^BOOL(NSString * _Nullable value) { @strongify(self); }]subscribeNext:^(NSString * _Nullable x) { @strongify(self); self.cellModel.valuePlaceParam=x; }];
|
多播的RACMulticastConnection(一对多)
将冷信号转变成热信号,并在合适的时间触发,向所有的订阅者发送消息
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| RACMulticastConnection *connection = [[RACSignal createSignal:^RACDisposable * _Nullable(id<RACSubscriber> _Nonnull subscriber) { NSLog(@"Send Request"); ... }] publish];
[connection.signal subscribeNext:^(id _Nullable x) { NSLog(@"product: %@", x); }]; [connection.signal subscribeNext:^(id _Nullable x) { NSNumber *productId = [x objectForKey:@"id"]; NSLog(@"productId: %@", productId); }];
[connection connect];
|
使用 -publish 方法生成实例,订阅者不再订阅源信号,而是订阅 RACMulticastConnection 中的 RACSubject 热信号,最后通过 -connect 方法触发源信号中的任务。
publish 和 multicast 方法
1 2 3 4 5 6 7 8 9 10
| - (RACMulticastConnection *)publish { RACSubject *subject = [RACSubject subject]; RACMulticastConnection *connection = [self multicast:subject]; return connection; }
- (RACMulticastConnection *)multicast:(RACSubject *)subject { RACMulticastConnection *connection = [[RACMulticastConnection alloc] initWithSourceSignal:self subject:subject]; return connection; }
|
当 -publish 方法调用时相当于向 -multicast: 传入了 RACSubject。
在使用 -multicast: 方法时,传入的信号其实就是用于广播的信号;这个信号必须是一个 RACSubject 本身或者它的子类
订阅源信号的时间点
只有在调用 -connect 方法之后,RACSubject 才会订阅源信号 sourceSignal。
1 2 3 4
| - (RACDisposable *)connect { self.serialDisposable.disposable = [self.sourceSignal subscribe:_signal]; return self.serialDisposable; }
|
这时源信号的 didSubscribe 代码块才会执行,向 RACSubject 推送消息,消息向下继续传递到 RACSubject 所有的订阅者中。
-connect
方法通过 -subscribe:
实际上建立了 RACSignal 和 RACSubject 之间的连接,这种方式保证了 RACSignal 中的 didSubscribe 代码块只执行了一次。
所有的订阅者不再订阅原信号,而是订阅 RACMulticastConnection 持有的热信号 RACSubject,实现对冷信号的一对多传播。
参考
- 『状态』驱动的世界:ReactiveCocoa