如何安全地测试一个方法是不是可以通过 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 被调用的主要内容,如果未能解决你的问题,请参考以下文章

如何一个接一个地运行 Specflow 测试而不是并行

如何安全地进行ddos压力测试?

如何安全地进行SQL注入测试

假设 GUID 始终是唯一的是不是安全?

有效地测试一个端口是不是在 Linux 上打开?

自动驾驶汽车要有国际安全测试标准了