[关闭]
@myron-lee 2017-03-22T12:34:03.000000Z 字数 6738 阅读 1246

RAC扩展──异步filter、map

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,提升效率。

1. asyncFilter

主体是flattenMap,构建新的Signal,新的Signal的数据源就是filterBlock,然后对新的Signal就行变换——先filter掉为NO的值,之后map回原来的值。

2. asyncMap

主体是flattenMap,返回新的Signal,新的Signal的数据源就是mapBlock,直接把值传递下去。


四、使用示例

设想一个业务场景: 选中一批订单=>选择一个用户=>二次确认是否要把订单移交给这个用户。选择一个用户这一步骤使用了asyncMap,二次确认使用了asyncFilter。

  1. -(RACSignal *)transferOrdersSignal{
  2. return [[[[[self selectedOrdersSignal]
  3. st_asyncMap:^(NSDictionary *selectedOrdersParams, id<RACSubscriber> subscriber) {
  4. [[self selectedCustomerSignal]
  5. subscribeNext:^(NSString *selectedCustomer) {
  6. if (selectedCustomer == nil) {
  7. [subscriber sendCompleted];
  8. } else {
  9. NSMutableDictionary *parmas = [selectedOrdersParams mutableCopy];
  10. [parmas setValue:selectedCustomer forKey:@"targetUserId"];
  11. [subscriber sendNext:[parmas copy]];
  12. }
  13. }];
  14. }]
  15. st_asyncFilter:^(NSDictionary *params, id<RACSubscriber> subscriber) {
  16. NSNumber *orderCount = params[@"orderCount"];
  17. AlertView *alertView = [[AlertView alloc]
  18. initWithmessage:[NSString stringWithFormat:@"是否确定移交此%@笔订单", orderCount]
  19. cancelTitle:@"取消"
  20. cancleColor:[UIColor whiteColor]
  21. otherTitle:@"确定"
  22. otherColor:[UIColor colorWithRGB:0xff9800]
  23. clickIndexHandle:^(NSInteger index) {
  24. if(index == 0){
  25. [subscriber sendNext:@(NO)];
  26. } else {
  27. [subscriber sendNext:@(YES)];
  28. }
  29. }];
  30. [alertView show];
  31. }]
  32. flattenMap:^RACStream *(NSDictionary *params) {
  33. //web request
  34. }]
  35. doError];
  36. }

五、源码

  1. - (instancetype)st_asyncFilter:(void (^)(id value, id<RACSubscriber> subscriber))block{
  2. return [self flattenMap:^RACStream *(id actualValue) {
  3. return [[[[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
  4. block(actualValue, subscriber);
  5. return nil;
  6. }] take:1
  7. ] filter:^BOOL(id filterFlagValue) {
  8. return [filterFlagValue boolValue];
  9. }] map:^id(id value) {
  10. return actualValue;
  11. }];
  12. }];
  13. }
  14. - (instancetype)st_asyncMap:(void (^)(id value, id<RACSubscriber> subscriber))block{
  15. return [self flattenMap:^RACStream *(id value) {
  16. return [[RACSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
  17. block(value, subscriber);
  18. return nil;
  19. }] take:1];;
  20. }];
  21. }

六、Update 2017-2-8

内部换了一种实现。采用了自定义的一次性信号── STOneTimeSignal。

  1. @implementation RACSignal (STExtension)
  2. - (instancetype)st_asyncFilter:(void (^)(id value, id<RACSubscriber> subscriber))block{
  3. return [self flattenMap:^RACStream *(id value) {
  4. return [[STOneTimeSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
  5. block(value, subscriber);
  6. return nil;
  7. }]
  8. flattenMap:^RACStream *(id flag) {
  9. if ([flag boolValue]) {
  10. return [RACSignal return:value];
  11. } else {
  12. return [RACSignal empty];
  13. }
  14. }];
  15. }];
  16. }
  17. - (instancetype)st_asyncMap:(void (^)(id value, id<RACSubscriber> subscriber))block{
  18. return [self flattenMap:^RACStream *(id value) {
  19. return [STOneTimeSignal createSignal:^RACDisposable *(id<RACSubscriber> subscriber) {
  20. block(value, subscriber);
  21. return nil;
  22. }];
  23. }];
  24. }

一次性信号── STOneTimeSignal的实现。STOneTimeSignal主要依赖STOneTimeSubscriber。

  1. @interface STOneTimeSignal : RACDynamicSignal
  2. @end
  3. @implementation STOneTimeSignal
  4. //Override subscribe method, use STOneTimeSubscriber to replace RACSubscriber
  5. //And make sure custom subscribe method get called
  6. - (RACDisposable *)subscribe:(id<RACSubscriber>)subscriber {
  7. NSCAssert(NO, @"This method is not implemented yet");
  8. return nil;
  9. }
  10. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock {
  11. NSCParameterAssert(nextBlock != NULL);
  12. //把外部Subscriber转化为OneTimeSubscriber
  13. STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:NULL completed:NULL];
  14. return [super subscribe:o];
  15. }
  16. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock {
  17. NSCParameterAssert(nextBlock != NULL);
  18. NSCParameterAssert(completedBlock != NULL);
  19. //把外部Subscriber转化为OneTimeSubscriber
  20. STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:NULL completed:completedBlock];
  21. return [super subscribe:o];
  22. }
  23. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock {
  24. NSCParameterAssert(nextBlock != NULL);
  25. NSCParameterAssert(errorBlock != NULL);
  26. NSCParameterAssert(completedBlock != NULL);
  27. //把外部Subscriber转化为OneTimeSubscriber
  28. STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
  29. return [super subscribe:o];
  30. }
  31. - (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock {
  32. NSCParameterAssert(errorBlock != NULL);
  33. //把外部Subscriber转化为OneTimeSubscriber
  34. STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:NULL error:errorBlock completed:NULL];
  35. return [super subscribe:o];
  36. }
  37. - (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock {
  38. NSCParameterAssert(completedBlock != NULL);
  39. //把外部Subscriber转化为OneTimeSubscriber
  40. STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:NULL error:NULL completed:completedBlock];
  41. return [super subscribe:o];
  42. }
  43. - (RACDisposable *)subscribeNext:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock {
  44. NSCParameterAssert(nextBlock != NULL);
  45. NSCParameterAssert(errorBlock != NULL);
  46. //把外部Subscriber转化为OneTimeSubscriber
  47. STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:errorBlock completed:NULL];
  48. return [super subscribe:o];
  49. }
  50. - (RACDisposable *)subscribeError:(void (^)(NSError *))errorBlock completed:(void (^)(void))completedBlock {
  51. NSCParameterAssert(completedBlock != NULL);
  52. NSCParameterAssert(errorBlock != NULL);
  53. //把外部Subscriber转化为OneTimeSubscriber
  54. STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:NULL error:errorBlock completed:completedBlock];
  55. return [super subscribe:o];
  56. }
  57. @end

STOneTimeSubscriber的实现。

  1. @interface STOneTimeSubscriber : NSObject <RACSubscriber>
  2. + (instancetype)subscriberWithNext:(void (^)(id x))next error:(void (^)(NSError *error))error completed:(void (^)(void))completed;
  3. @end
  4. @interface STOneTimeSubscriber ()
  5. //...
  6. @end
  7. @implementation STOneTimeSubscriber
  8. //...
  9. #pragma mark RACSubscriber
  10. - (void)sendNext:(id)value {
  11. @synchronized (self) {
  12. void (^nextBlock)(id) = [self.next copy];
  13. if (nextBlock == nil) return;
  14. nextBlock(value);
  15. //complete automatically when receive value, that's why this's called one-time subscriber.
  16. //you can accomplish the same function using `take` operation. but that's more expensive
  17. [self sendCompleted];
  18. }
  19. }
  20. //...
  21. @end
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注