我们在使用RAC的时候,有时候处理一个主消息之外可能还需要其他的辅助消息。比如说,我们在上次图片或者下载的时候。往往除了需要知道结果,还需要过程的进度。简单的做法就是外部自己创建一个subject,然后给具体做事情的模块来手动设置subject的next值了。这是一个经常用的东西,所以我这边参考了AFNetworking+RACExtension中得代码。自己重建了支持进度的signal和subscribe。我是在RAC 2.4.2上面搞的。所以不一定支持其他版本的哦。 废话不说,直接上代码。
#import "ReactiveCocoa.h"
@interface KLRACSubscriber: NSObject
+ (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
#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 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 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 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)(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 subscriber) = ^(id 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