我仍然试图围绕ReactiveCocoa,尤其是一件事令人头疼。我想有一个CoreBluetooth API的声明性接口,让我做这样的事情:
@property (nonatomic) NSNumber *registerValue;
@property (nonatomic) NSNumber *serialNumber;
RACSignal *characteristics = [[RACObserve(self, connectedPeripheral) flattenMap:^RACStream *(CBPeripheral *peripheral) {
return [peripheral rac_discoverServices:@[[CBUUID UUIDWithString:kBLEServiceUUIDString]]];
}] flattenMap:^RACStream *(CBService *service) {
return [service rac_discoverCharacteristics:nil];
}];
RACSignal *devInfoCharacteristics = [[RACObserve(self, connectedPeripheral) flattenMap:^RACStream *(CBPeripheral *peripheral) {
return [peripheral rac_discoverServices:@[[CBUUID UUIDWithString:kDevInfoServiceUUIDString]]];
}] flattenMap:^RACStream *(CBService *service) {
return [service rac_discoverCharacteristics:nil];
}];
RACSignal *registerUpdates = [[[characteristics filter:^BOOL(CBCharacteristic *characteristic) {
return ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kRegisterCharacteristicUUIDString]]);
}] flattenMap:^RACStream *(CBCharacteristic *characteristic) {
return [[characteristic rac_updateNotificationState:YES] mapReplace:characteristic];
}] flattenMap:^RACStream *(CBCharacteristic *characteristic) {
return [characteristic rac_didUpdateValue];
}];
RACSignal *serialNumberSignal = [[[characteristics filter:^BOOL(CBCharacteristic *characteristic) {
return ([characteristic.UUID isEqual:[CBUUID UUIDWithString:kSerialNumberCharacteristicUUIDString]]);
}] flattenMap:^RACStream *(CBCharacteristic *characteristic) {
return [characteristic rac_didUpdateValue];
}];
RAC(self, serialNumber) = serialNumberSignal;
RAC(self, registerValue) = registerUpdates;
现在,rac_discoverServices的实现看起来像这样(CBPeripheral上的一个类别):
- (RACSignal *)rac_discoverServices:(NSArray *)serviceUUIDs
{
@weakify(self);
return [RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
@strongify(self);
RACDisposable *disposable = [[[[self rac_didDiscoverServices] take:1] flattenMap:^RACStream *(id value) {
return [[[self.services rac_sequence] signal] filter:^BOOL(CBService *service) {
if (!serviceUUIDs) {
return YES;
} else {
for (CBUUID *uuid in serviceUUIDs) {
if ([uuid isEqual:service.UUID]) {
return YES;
}
}
return NO;
}
}];
}] subscribe:subscriber];
[self discoverServices:serviceUUIDs];
return disposable;
}];
}
问题在于我不会认为这可以工作,因为我不相信可以保证回调订购。所以我真正想要的是能够确保在第一个信号完成之前,第二个信号的订阅无法发出调用以发现新服务。
我知道RACCommand会阻止并发操作,但我没有看到使用它来确保操作排队的方法。
我缺少一些明显的解决方案吗?我唯一能想到的就是以某种方式维持一个呼叫队列,并在先前的信号被处理时再次调用发现(并且我不确定它是否会起作用)。