平时调用delegate的时候,都会做一些安全检查,比如
if ([self.delegate respondsToSelector:@selector(recordDidProcessedNewVideoSample:)])
{
[self.delegate recordDidProcessedNewVideoSample:sampleBuffer];
}
是不是比较麻烦?我实现了一个宏,方便调用delegate。
#define SAFE_CALL_DELEGATE(delegate,aSelector,args...) callDelegate(delegate,aSelector,##args)
其中的callDelegate函数实现如下 :
void callDelegate(id delegate,SEL selector,...)
{
if(delegate&&selector&&[delegate respondsToSelector:selector])
{
NSMethodSignature *signature =[[delegate class] instanceMethodSignatureForSelector:selector];
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:delegate];
[invocation setSelector:selector];
// 0:self,1:SEL,所以参数从2开始
const int argStartIndexOffset = 2;
if (signature.numberOfArguments - argStartIndexOffset > 0)
{
int argIndexOffset = 0;
va_list args;
va_start(args, selector);
while (argIndexOffset < signature.numberOfArguments - argStartIndexOffset)
{
const char *type = [signature getArgumentTypeAtIndex:argIndexOffset + argStartIndexOffset];
if (strcmp(type, "i") == 0) // int
{
int64_t arg = va_arg(args, int64_t);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "c") == 0) // char
{
int64_t arg = va_arg(args, int64_t);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "s") == 0) // short
{
int64_t arg = va_arg(args, int64_t);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "l") == 0) // long
{
long arg = va_arg(args, long);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "q") == 0) // long long
{
long long arg = va_arg(args,long long);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "f") == 0) // float
{
double arg = va_arg(args, double);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "d") == 0) // double
{
double arg = va_arg(args, double);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "B") == 0) // bool
{
int arg = va_arg(args, int);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, "*") == 0) // char *
{
char * arg = va_arg(args, char*);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else if (strcmp(type, ":") == 0) // SEL
{
SEL arg = va_arg(args, SEL);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
else // v,@,[array type]等
{
id arg = va_arg(args, id);
[invocation setArgument:&arg atIndex:argIndexOffset + argStartIndexOffset];
}
argIndexOffset++;
}
va_end(args);
}
[invocation invoke];
}
}
如果想实现返回值,建议用block的方式,作为delegate的参数,类似- (void)callWithBlock:(void(^)(int))block;
测试用例:
@protocol TestProtocol- (void)arg1:(NSString*)arg1 arg2:(NSNumber*)arg2 arg3:(ViewController*)arg3 arg4:(int)arg4;
- (void)noArg;
- (void)callWithBlock:(void(^)(int))block;
- (void)intarg:(int)arg1 chararg:(char)arg2 boolarg:(BOOL)arg3 selectorarg:(SEL)arg4 charstararg:(char*)arg5;
@end
调用方式:
void(^block)(int) = ^(int a)
{
NSLog(@"from block:%d",a);
};
SAFE_CALL_DELEGATE(self.delegate, @selector(callWithBlock:),block);
SAFE_CALL_DELEGATE(self.delegate, @selector(noArg));
SAFE_CALL_DELEGATE(self.delegate, @selector(intarg:chararg:boolarg:selectorarg:charstararg:),2,'a',NO,@selector(viewDidLoad),"hello world");