@myron-lee
2017-03-22T12:34:03.000000Z
字数 6738
阅读 1341
iOS Blog
RAC有非常丰富的Operation,可以让我们的开发事半功倍。常用的比如:filter、map。但是原生的filter、map要求我们在Block同步返回结果。但是,有的时候,我们只能异步返回结果。比如我们需要根据用户的输入进行filter(二次确认);比如我们需要先请求网络才能进行map。
所以,我对原有的Operation进行了扩展,开发了asyncFilter、asyncMap两个新的Operation。
asyncFilter、asyncMap的入参类型是void (^)(id value, id<RACSubscriber> subscriber)的Block,与原始filter、map直接return结果不同,asyncFilter、asyncMap通过subscriber将结果发送出来。asyncFilter在block中[subscriber sendNext:@(YES)]; / [subscriber sendNext:@(NO)];,asyncMap在block中[subscriber sendNext:yourMappedValue];。注意发送error事件会导致整个observe结束,我并没有做防护,因为考虑到调用方在遇到异常情况的时候,可能需要直接结束整个observe。
因为异步了,filter、map Block都不会直接返回结果了,结果将会通过一个subscriber返回(这个subscriber是Block的入参,Operation是会塞给Block),此时这个Block也就变成了一个数据源。
内部是把原Signal flattenMap成新的Signal,新的Signal的数据源就是这个Block(把这个Block罩个壳就是新Signal)。
我们知道,flattenMap会把原Signal的每一个value都map成一个新的Signal,在我们的场景里,新的Signal一定只有一个value,所以我asyncFilter、asyncMap两个Operation接收到一个值的时候会自动complete,调用方在发送@(YES)/@(NO)/@(mappedValue)之后不必再手动发送一个complete事件。及时complete掉Signal,dispose掉Signal,提升效率。
主体是flattenMap,构建新的Signal,新的Signal的数据源就是filterBlock,然后对新的Signal就行变换——先filter掉为NO的值,之后map回原来的值。
主体是flattenMap,返回新的Signal,新的Signal的数据源就是mapBlock,直接把值传递下去。
设想一个业务场景: 选中一批订单=>选择一个用户=>二次确认是否要把订单移交给这个用户。选择一个用户这一步骤使用了asyncMap,二次确认使用了asyncFilter。
-(RACSignal *)transferOrdersSignal{return [[[[[self selectedOrdersSignal]st_asyncMap:^(NSDictionary *selectedOrdersParams, id<RACSubscriber> subscriber) {[[self selectedCustomerSignal]subscribeNext:^(NSString *selectedCustomer) {if (selectedCustomer == nil) {[subscriber sendCompleted];} else {NSMutableDictionary *parmas = [selectedOrdersParams mutableCopy];[parmas setValue:selectedCustomer forKey:@"targetUserId"];[subscriber sendNext:[parmas copy]];}}];}]st_asyncFilter:^(NSDictionary *params, id<RACSubscriber> subscriber) {NSNumber *orderCount = params[@"orderCount"];AlertView *alertView = [[AlertView alloc]initWithmessage:[NSString stringWithFormat:@"是否确定移交此%@笔订单", orderCount]cancelTitle:@"取消"cancleColor:[UIColor whiteColor]otherTitle:@"确定"otherColor:[UIColor colorWithRGB:0xff9800]clickIndexHandle:^(NSInteger index) {if(index == 0){[subscriber sendNext:@(NO)];} else {[subscriber sendNext:@(YES)];}}];[alertView show];}]flattenMap:^RACStream *(NSDictionary *params) {//web request}]doError];}
- (instancetype)st_asyncFilter:(void (^)(id value, id<RACSubscriber> subscriber))block{return [self flattenMap:^RACStream *(id actualValue) {return [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {block(actualValue, subscriber);return nil;}] take:1] filter:^BOOL(id filterFlagValue) {return [filterFlagValue boolValue];}] map:^id(id value) {return actualValue;}];}];}- (instancetype)st_asyncMap:(void (^)(id value, id<RACSubscriber> subscriber))block{return [self flattenMap:^RACStream *(id value) {return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {block(value, subscriber);return nil;}] take:1];;}];}
内部换了一种实现。采用了自定义的一次性信号── STOneTimeSignal。
@implementation RACSignal (STExtension)- (instancetype)st_asyncFilter:(void (^)(id value, id<RACSubscriber> subscriber))block{return [self flattenMap:^RACStream *(id value) {return [[STOneTimeSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {block(value, subscriber);return nil;}]flattenMap:^RACStream *(id flag) {if ([flag boolValue]) {return [RACSignal return:value];} else {return [RACSignal empty];}}];}];}- (instancetype)st_asyncMap:(void (^)(id value, id<RACSubscriber> subscriber))block{return [self flattenMap:^RACStream *(id value) {return [STOneTimeSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {block(value, subscriber);return nil;}];}];}
一次性信号── STOneTimeSignal的实现。STOneTimeSignal主要依赖STOneTimeSubscriber。
@interface STOneTimeSignal : RACDynamicSignal@end@implementation STOneTimeSignal//Override subscribe method, use STOneTimeSubscriber to replace RACSubscriber//And make sure custom subscribe method get called- (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {NSCAssert(NO, @"This method is not implemented yet");return nil;}- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {NSCParameterAssert(nextBlock != NULL);//把外部Subscriber转化为OneTimeSubscriberSTOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];return [super subscribe:o];}- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock {NSCParameterAssert(nextBlock != NULL);NSCParameterAssert(completedBlock != NULL);//把外部Subscriber转化为OneTimeSubscriberSTOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:NULL completed:completedBlock];return [super subscribe:o];}- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {NSCParameterAssert(nextBlock != NULL);NSCParameterAssert(errorBlock != NULL);NSCParameterAssert(completedBlock != NULL);//把外部Subscriber转化为OneTimeSubscriberSTOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];return [super subscribe:o];}- (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock {NSCParameterAssert(errorBlock != NULL);//把外部Subscriber转化为OneTimeSubscriberSTOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:NULL error:errorBlock completed:NULL];return [super subscribe:o];}- (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock {NSCParameterAssert(completedBlock != NULL);//把外部Subscriber转化为OneTimeSubscriberSTOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:NULL error:NULL completed:completedBlock];return [super subscribe:o];}- (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock {NSCParameterAssert(nextBlock != NULL);NSCParameterAssert(errorBlock != NULL);//把外部Subscriber转化为OneTimeSubscriberSTOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:errorBlock completed:NULL];return [super subscribe:o];}- (RACDisposable *)subscribeError:(void (^)(NSError *))errorBlock completed:(void (^)(void))completedBlock {NSCParameterAssert(completedBlock != NULL);NSCParameterAssert(errorBlock != NULL);//把外部Subscriber转化为OneTimeSubscriberSTOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:NULL error:errorBlock completed:completedBlock];return [super subscribe:o];}@end
STOneTimeSubscriber的实现。
@interface STOneTimeSubscriber : NSObject <RACSubscriber>+ (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed;@end@interface STOneTimeSubscriber ()//...@end@implementation STOneTimeSubscriber//...#pragma mark RACSubscriber- (void)sendNext:(id)value {@synchronized (self) {void (^nextBlock)(id) = [self.next copy];if (nextBlock == nil) return;nextBlock(value);//complete automatically when receive value, that's why this's called one-time subscriber.//you can accomplish the same function using `take` operation. but that's more expensive[self sendCompleted];}}//...@end