一、前言
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。
-(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];;
}];
}
六、Update 2017-2-8
内部换了一种实现。采用了自定义的一次性信号── 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转化为OneTimeSubscriber
STOneTimeSubscriber *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转化为OneTimeSubscriber
STOneTimeSubscriber *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转化为OneTimeSubscriber
STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:nextBlock error:errorBlock completed:completedBlock];
return [super subscribe:o];
}
- (RACDisposable *)subscribeError:(void (^)(NSError *error))errorBlock {
NSCParameterAssert(errorBlock != NULL);
//把外部Subscriber转化为OneTimeSubscriber
STOneTimeSubscriber *o = [STOneTimeSubscriber subscriberWithNext:NULL error:errorBlock completed:NULL];
return [super subscribe:o];
}
- (RACDisposable *)subscribeCompleted:(void (^)(void))completedBlock {
NSCParameterAssert(completedBlock != NULL);
//把外部Subscriber转化为OneTimeSubscriber
STOneTimeSubscriber *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转化为OneTimeSubscriber
STOneTimeSubscriber *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转化为OneTimeSubscriber
STOneTimeSubscriber *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