检查ObjC选择器(SEL)上是否有参数

时间:2017-06-26 01:30:36

标签: objective-c selector introspection nsinvocation

就像UIButton我可以:

[button addTarget:target forSelector:@selector(action)]

或者:

[button addTarget:target forSelector:@selector(action:)]

在第二个按钮中,按钮的动作将有一个发送者(作为按钮本身),如何确定给定的选择器是否有参数,以便我可以传递给它?

2 个答案:

答案 0 :(得分:3)

作为第一关,我会使用NSStringFromSelector()。如果出于某种原因给你带来问题,我会使用sel_getName

NSInteger MYParameterCountOfSelector(SEL selector) {
    NSString *string = NSStringFromSelector(selector);
    return [string componentsSeparatedByString:@":"].count - 1;
}

答案 1 :(得分:2)

杰弗瑞是对的(很多动态的ObjC都是通过这种方式搞乱选择器来处理,而不是潜入NSMethodSignature的胆量,这可以为你提供更精确的信息。但同样,你总能传递一些东西。我知道这听起来很疯狂,但是传递太多东西实际上并不是ObjC的问题。

@interface MyObject: NSObject
- (void)something;
@end

...

MyObject *object = [MyObject new];
[object performSelector:@selector(something) withObject:@"thing"];

工作正常。什么!?!?是啊。这实际上是您的按钮示例的工作原理。在IBAction中放置一个断点,然后查看来电者:

0x108b21d2c <+58>: movq   0x1400a1d(%rip), %rsi     ; "performSelector:withObject:withObject:"
0x108b21d33 <+65>: movq   %rbx, %rdi
0x108b21d36 <+68>: movq   %r12, %rdx
0x108b21d39 <+71>: movq   %r15, %rcx
0x108b21d3c <+74>: movq   %r14, %r8
0x108b21d3f <+77>: callq  *0x10ac7f3(%rip)          ; (void *)0x0000000108019ac0: objc_msgSend

它始终会调用performSelector:withObject:withObject:,即使您没有要求,也会将发件人和事件传递给您。如果你不相信我,请尝试查看$rdx(arg2)和$rcx(arg3)(假设你在模拟器中。如果你想要所有正确的寄存器,你需要{{ 3}})。来自我的(IBAction)doThing方法:

(lldb) po $rdx
<UIButton: 0x7f8071a126f0; frame = (164 318; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x608000223140>>

(lldb) po $rcx
<UITouchesEvent: 0x6180001068a0> timestamp: 1.06651e+06 touches: {(
    <UITouch: 0x7f8070500e20> phase: Ended tap count: 1 force: 0.000 window: <UIWindow: 0x7f8071a10890; frame = (0 0; 414 736); autoresize = W+H; gestureRecognizers = <NSArray: 0x60800026dbc0>; layer = <UIWindowLayer: 0x608000222800>> view: <UIButton: 0x7f8071a126f0; frame = (164 318; 46 30); opaque = NO; autoresize = RM+BM; layer = <CALayer: 0x608000223140>> location in window: {205, 335} previous location in window: {205, 335} location in view: {41, 17} previous location in view: {41, 17}
)}

所以是的,你可以撕开选择器并找出发送或不发送的内容,但是在Cocoa中执行此操作的典型方法就是设置,这样你就可以随时发送所有内容并让接收者忽略它。 / p>