学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

ReactiveObjC 使用

ReactiveCocoa 在设计上很大程度借鉴了 Reactive Extension 中的概念,可以说 ReactiveCocoa 是 Rx 在 Objective-C 语言中的实现

Rx 中主要的两个概念信号和序列都在 ReactiveCocoa 中有着相对应的组件 RACSignalRACSequence

RACSignal 和 RACSequence

最大区别就是:

  1. RACSignal 是推驱动的,也就是在每次信号中的出现新的数据时,所有的订阅者都会自动接受到最新的值;

  2. 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 协议的对象列表以及所有的消息,
  • RACSubject 对外的接口
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

//冷信号 RACSingal
-(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
// 热信号 RACSubject
// 直接指派
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 接口中的高阶信号

它在接口中暴露出的四个信号

  1. executionSignals
    在每次执行 -execute: 方法时,最终都会向 executionSignals 中传入一个最新的信号

  2. executing
    executing 是一个表示当前是否有任务执行的信号

  3. enabled

enabled 信号流表示当前的命令是否可以再次被执行,也就是 -execute: 方法能否可以成功执行新的任务;

  1. 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
//下单 上门信息   ViewModel
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);

// View WSFPlaceOrderSingleLineInputCell
-(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,实现对冷信号的一对多传播。

参考

  1. 『状态』驱动的世界:ReactiveCocoa