如何安全地测试一个方法是不是可以通过 NSInvocation 被调用
Posted
技术标签:
【中文标题】如何安全地测试一个方法是不是可以通过 NSInvocation 被调用【英文标题】:How to safely test whether a method can be called through NSInvocation如何安全地测试一个方法是否可以通过 NSInvocation 被调用 【发布时间】:2015-05-31 06:45:51 【问题描述】:我已经使用 ObjC 运行时生成了一个类的方法和属性列表,以便以后可以使用 NSInvocation 从桥接器调用这些方法和属性。
问题在于,对于那些运行时无法生成签名的方法,我遇到了错误。
例如在SKFieldNode
的实例中调用direction
的属性getter 抛出异常NSInvalidArgumentException
,我猜这是因为vector_float3
没有编码类型,它的类型是''(即没有字符类型)
这是测试我所描述的内容的方法:
Method method = class_getInstanceMethod([SKFieldNode class], @selector(direction));
const char *types = method_getTypeEncoding(method);
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:types];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
SKFieldNode *field = [SKFieldNode node];
field.direction = (vector_float3)1,2,3;
[inv setTarget:field];
[inv setSelector:@selector(direction)]; // (*)
[inv invoke];
vector_float3 v;
[inv getReturnValue:&v];
NSLog(@"%f %f %f", v.x, v.y, v.z);
(*) "NSInvalidArgumentException", "-[NSInvocation setArgument:atIndex:]: 索引 (1) 超出范围 [-1, 0]"
如何使用自省来判断一个方法是否可以安全地以这种方式调用?
我尝试测试NSMethodSignature
返回的参数数量,但是对于缺少编码类型的方法,该值是错误的,例如这两个方法将返回 2,计算目标和选择器,因此剩余的参数是不考虑。
- setDirection:(vector_float3)d1 direction:(vector_float3)d2;
- setDirection:(vector_float3)d;
我还注意到方向属性is not available in Swift
这让我觉得是因为同样的原因。所以我不介意在自定义桥中放弃对这些方法的支持。
【问题讨论】:
相关:***.com/questions/22959915/… 【参考方案1】:这是一个相当简单的检查,可确保您没有任何编码不当的参数:
BOOL isMethodSignatureValidForSelector(NSMethodSignature *signature, SEL selector)
// This could break if you use a unicode selector name, so please don't do that :)
const char *c_str = sel_getName(selector);
unsigned numberOfArgs = 2;
while (*c_str)
if (*c_str == ':')
numberOfArgs++;
;
c_str++;
return ([signature numberOfArguments] == numberOfArgs);
【讨论】:
NSStringFromSelector(selector).UTF8String
是否可以解决 unicode 选择器名称的问题?不是我想使用一个,只是出于好奇
谢谢,你的回答很聪明,我最终使用了selectorName = NSStringFromSelector(selector)
和numberOfArgs = 1 + [selectorName componentsSeparatedByString:@":"].count
【参考方案2】:
您可以使用try-catch
语句来尝试运行一些代码:
@try
Method method = class_getInstanceMethod([SKFieldNode class], @selector(direction));
const char *types = method_getTypeEncoding(method);
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:types];
NSInvocation *inv = [NSInvocation invocationWithMethodSignature:sig];
SKFieldNode *field = [SKFieldNode node];
field.direction = (vector_float3)1,2,3;
[inv setTarget:field];
[inv setSelector:@selector(direction)]; // (*)
[inv invoke];
vector_float3 v;
[inv getReturnValue:&v];
NSLog(@"%f %f %f", v.x, v.y, v.z);
@catch (NSException *exception)
【讨论】:
我更喜欢自省,这样我就可以从列表中排除这些方法并使用 NSInvocation 安全地调用其余方法 您可以将 try/catch 放入方法中作为检查器。不过,如果你知道更好的方法,请告诉我:) 尝试 catch 不是一个好的解决方案,因为 ARC 不能很好地使用它,除非你打开那个编译器标志,这会极大地增加你的二进制文件大小。 @RichardJ.RossIII 谢谢!!以上是关于如何安全地测试一个方法是不是可以通过 NSInvocation 被调用的主要内容,如果未能解决你的问题,请参考以下文章