I have a class called "MyUploadClient" for handling video upload task. "MyUploadClient" has a protocol called "MyUploadClientDelegate". Someone use "MyUploadClient" to upload video needs to comfort to this protocol, and the delegate may be more than one, so I make a property called 'delegateSet' to store the delegates. The method '-invokeDelegateItemsWithAction:withObjects:...' is used to wrap the delegate method and invoke when it needs to.
But the delegate method "-uploadTask:progressChange:" has a parameter which type is float, the app crash when it execute "va_arg(args, id)".
To solve this problem, I change the parameter from float to NSNumber, but the app still crashed when execute "[invocation invoke]" with error "EXC_BAD_ACCESS".
Why did this happen and how can I fix this problem?
Relative code In file: MyUploadClient.h
@protocol MyUploadClientDelegate <NSObject>
- (void)startUploadTask:(MyVideo *)video;
- (void)didUploadTask:(MyVideo *)video error:(NSError * __nullable)error;
- (void)uploadTask:(NSString *)videoId progressChange:(float)progress;
……
@end
@interface MyUploadClient : NSObject
+ (instancetype)sharedClient;
- (void)addDelegate:(id<MyUploadClientDelegate>)delegate;
- (void)removeDelegate:(id<MyUploadClientDelegate>)delegate;
……
@end
Relative code In file: MyUploadClient.m
@interface MyUploadClientDelegateItem : NSObject
@property (nonatomic, weak) id<MyUploadClientDelegate> delegate;
- (instancetype)initWithDelegate:(id<MyUploadClientDelegate>)delegate;
@end
@implementation MyUploadClientDelegateItem
- (instancetype)initWithDelegate:(id<MyUploadClientDelegate>)delegate {
self = [super init];
if (self) {
[self setDelegate:delegate];
}
return self;
}
@end
@interface MyUploadClient ()
@property (nonatomic, strong) NSMutableSet *delegateSet;
@end
@implementation MyUploadClient
- (NSMutableSet *)delegateSet {
if (!_delegateSet) {
_delegateSet = [NSMutableSet set];
}
return _delegateSet;
}
- (void)addDelegate:(id<MyUploadClientDelegate>)delegate {
for (MyUploadClientDelegateItem *item in self.delegateSet) {
if (delegate == item.delegate) {
return ;
}
}
MyUploadClientDelegateItem *delegateItem = [[MyUploadClientDelegateItem alloc] initWithDelegate:delegate];
[self.delegateSet addObject:delegateItem];
}
- (void)removeDelegate:(id<MyUploadClientDelegate>)delegate {
MyUploadClientDelegateItem *deleteItem = nil;
for (MyUploadClientDelegateItem *item in self.delegateSet) {
if (delegate == item.delegate) {
deleteItem = item;
}
}
if (deleteItem) {
[self.delegateSet removeObject:deleteItem];
}
}
- (void)startUploadWithVideo:(MyVideo *)video {
……
[self invokeDelegateItemsWithAction:@selector(startUploadTask:) withObjects:video, nil];
……
}
- (void)uploadProgressChanged:(float)progress videoId:(NSString *)videoId {
……
[self invokeDelegateItemsWithAction:@selector(uploadTask:progressChange:) withObjects:videoId, progress, nil];
……
}
- (void)invokeDelegateItemsWithAction:(SEL)action withObjects:(id)firstObj, ... NS_REQUIRES_NIL_TERMINATION {
NSMutableArray *deleteItems = [NSMutableArray arrayWithCapacity:0];
NSSet *copySet = [NSSet setWithSet:self.delegateSet];
for (MyDelegateItem *item in copySet) {
if (item.delegate && [item.delegate respondsToSelector:action]) {
NSMethodSignature *sigOfAction = [(id)item.delegate methodSignatureForSelector:action];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:sigOfAction];
[invocation setTarget:item.delegate];
[invocation setSelector:action];
if (firstObj) {
NSInteger argsIndex = 2;
va_list args;
va_start(args, firstObj);
id argObject = firstObj;
while (argObject) {
if ([argObject isKindOfClass:[NSValue class]]) {
void *value;
[argObject getValue:&value];
[invocation setArgument:&value atIndex:argsIndex];
} else {
[invocation setArgument:&argObject atIndex:argsIndex];
}
argObject = va_arg(args, id);
argsIndex++;
}
va_end(args);
}
[invocation invoke];
}
if (!item.delegate) {
[deleteItems addObject:item];
}
}
for (id obj in deleteItems) {
[self.delegateSet removeObject:obj];
}
}
@end