我们在使用RAC的时候,有时候处理一个主消息之外可能还需要其他的辅助消息。比如说,我们在上次图片或者下载的时候。往往除了需要知道结果,还需要过程的进度。简单的做法就是外部自己创建一个subject,然后给具体做事情的模块来手动设置subject的next值了。这是一个经常用的东西,所以我这边参考了AFNetworking+RACExtension中得代码。自己重建了支持进度的signal和subscribe。我是在RAC 2.4.2上面搞的。所以不一定支持其他版本的哦。 废话不说,直接上代码。
#import "ReactiveCocoa.h" @interface KLRACSubscriber: NSObject<RACSubscriber> + (instancetype)subscriberWithNext:(void (^)(id x))next progress:(void (^)(float progress))progress error:(void (^)(NSError *error))error completed:(void (^)(void))completed; - (void)sendProgress:(float)p; - (void)sendNext:(id)value; - (void)sendError:(NSError *)error; - (void)sendCompleted; - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable; @end @interface RACSignal (KLProgressSubscriptions) // Convenience method to subscribe to the `progress` and `next` events. - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock ; // Convenience method to subscribe to the `progress`,`next` and `completed` events. - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock; // Convenience method to subscribe to the `progress`,`next`,`completed`,and `error` events. - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock; - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress completed:(void (^)(void))completedBlock; // Convenience method to subscribe to `progress`,`next` and `error` events. - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock; // Convenience method to subscribe to `progress`,`error` and `completed` events. - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock; @end @interface RACSubject (KLProgressSending) - (void)sendProgress:(float)value; @end
#import "RACSubscriber+KLProgressSupport.h" #import "RACPassthroughSubscriber.h" #import <objc/runtime.h> #import "KLLog.h" @interface RACPassthroughSubscriber (KLProgress) - (void)sendProgress:(float)p; @end @implementation RACPassthroughSubscriber(KLProgress) - (void)sendProgress:(float)p{ RACDisposable *disposable = [self performSelector:@selector(disposable)]; if (disposable.disposed) return; id<RACSubscriber> innerSubscriber = [self valueForKey:@"innerSubscriber"]; if([innerSubscriber isKindOfClass:[RACPassthroughSubscriber class]]){ [(RACPassthroughSubscriber*)innerSubscriber sendProgress:p]; }else if([innerSubscriber isKindOfClass:[KLRACSubscriber class]]){ [(KLRACSubscriber*)innerSubscriber sendProgress:p]; }else{ NSAssert(0,@"not recognized object"); } } @end static NSString *KLProgress_Block_Key; @interface KLRACSubscriber() @property(nonatomic,strong)id<RACSubscriber> subscriber; @property (nonatomic,copy) void (^_progress)(float progress); @end @implementation KLRACSubscriber + (instancetype)subscriberWithNext:(void (^)(id x))next progress:(void (^)(float progress))progress error:(void (^)(NSError *error))error completed:(void (^)(void))completed { Class subscriberCls = NSClassFromString(@"RACSubscriber"); SEL createSel = sel_registerName("subscriberWithNext:error:completed:"); static int supportCreate = -1; id<RACSubscriber> proxySubscriber = nil; void *obj = nil; if(-1 == supportCreate){ supportCreate = [subscriberCls respondsToSelector:createSel]; } if(YES == supportCreate){ NSMethodSignature *sig= [subscriberCls methodSignatureForSelector:createSel]; if(sig && !strcmp(sig.methodReturnType,@encode(id))){ NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sig]; [invocation setTarget:subscriberCls]; [invocation setSelector:createSel]; next = [next copy]; error = [error copy]; completed = [completed copy]; [invocation setArgument:&next atIndex: 2]; [invocation setArgument:&error atIndex: 3]; [invocation setArgument:&completed atIndex: 4]; [invocation retainArguments]; [invocation invoke]; [invocation getReturnValue:&obj]; proxySubscriber = (__bridge id<RACSubscriber>)(obj); } } KLRACSubscriber *subscriber = nil; if(proxySubscriber){ subscriber = [[KLRACSubscriber alloc]init]; subscriber.subscriber = proxySubscriber; subscriber._progress = progress; }else{ NSAssert(0,@"not create RACSubscriber"); } return subscriber; } - (void)set_progress:(void (^)(float))_progress { objc_setAssociatedObject(self,&KLProgress_Block_Key,_progress,OBJC_ASSOCIATION_COPY); } - (void (^)(float))_progress { return objc_getAssociatedObject(self,&KLProgress_Block_Key); } - (void)sendProgress:(float)p { RACDisposable *disposable = [self performSelector:@selector(disposable)]; if(disposable.disposed) return; if (self._progress != NULL) self._progress(p); } - (void)sendNext:(id)value{ return [self.subscriber sendNext:value]; } - (void)sendError:(NSError *)error{ return [self.subscriber sendError:error]; } - (void)sendCompleted{ return [self.subscriber sendCompleted]; } - (void)didSubscribeWithDisposable:(RACCompoundDisposable *)disposable{ [self.subscriber didSubscribeWithDisposable:disposable]; } - (void)forwardInvocation:(NSInvocation *)invocation{ NSAssert(self.subscriber,@"subscriber is nil"); if(!self.subscriber) KLLogError(@"subscriber is nil"); [invocation invokeWithTarget:self.subscriber]; } - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel{ NSAssert(self.subscriber,@"subscriber is nil"); if(!self.subscriber) KLLogError(@"subscriber is nil:%@",NSStringFromSelector(sel)); NSObject* tmpSub = self.subscriber; return [tmpSub methodSignatureForSelector:sel]; } - (void)dealloc{ self.subscriber = nil; } @end @implementation RACSignal (KLProgressSubscriptions) - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock { NSParameterAssert(progress != NULL); NSParameterAssert(nextBlock != NULL); KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:NULL completed:NULL]; return [self subscribe:o.subscriber]; } - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock completed:(void (^)(void))completedBlock { NSParameterAssert(progress != NULL); NSParameterAssert(nextBlock != NULL); NSParameterAssert(completedBlock != NULL); KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:NULL completed:completedBlock]; return [self subscribe:o]; } - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock { NSParameterAssert(progress != NULL); NSParameterAssert(nextBlock != NULL); NSParameterAssert(errorBlock != NULL); NSParameterAssert(completedBlock != NULL); KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:errorBlock completed:completedBlock]; return [self subscribe:o]; } - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress completed:(void (^)(void))completedBlock { NSParameterAssert(progress != NULL); NSParameterAssert(completedBlock != NULL); KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:NULL progress:progress error:NULL completed:completedBlock]; return [self subscribe:o]; } - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress next:(void (^)(id x))nextBlock error:(void (^)(NSError *error))errorBlock { NSParameterAssert(progress != NULL); NSParameterAssert(nextBlock != NULL); NSParameterAssert(errorBlock != NULL); KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:nextBlock progress:progress error:errorBlock completed:NULL]; return [self subscribe:o]; } - (RACDisposable *)subscribeProgress:(void (^)(float progress))progress error:(void (^)(NSError *error))errorBlock completed:(void (^)(void))completedBlock { NSParameterAssert(progress != NULL); NSParameterAssert(errorBlock != NULL); NSParameterAssert(completedBlock != NULL); KLRACSubscriber *o = [KLRACSubscriber subscriberWithNext:NULL progress:progress error:errorBlock completed:completedBlock]; return [self subscribe:o]; } @end @implementation RACSubject (KLProgressSending) - (void)sendProgress:(float)value { void (^subscriberBlock)(id<RACSubscriber> subscriber) = ^(id<RACSubscriber> subscriber){ if([subscriber isKindOfClass:[RACPassthroughSubscriber class]]){ [(RACPassthroughSubscriber*)subscriber sendProgress:value]; } }; SEL performBlockSel = sel_registerName("enumerateSubscribersUsingBlock:"); if([self respondsToSelector:performBlockSel]){ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Warc-performSelector-leaks" [self performSelector:performBlockSel withObject:subscriberBlock]; #pragma clang diagnostic pop }else{ NSAssert(0,@"not found enumerateSubscribersUsingBlock:"); } } @end