Just like UIButton
where I can:
[button addTarget:target forSelector:@selector(action)]
Or:
[button addTarget:target forSelector:@selector(action:)]
Where in the second one the action for the button will have a sender (being the button itself), how do I determine if a given selector has parameter, so that I can pass something to it?
Jeffery is right (and lots of dynamic ObjC is handled by messing with selectors this way rather than diving into the guts of NSMethodSignature
, which gives you much more precise information). But equally, you can always pass something. I know that sounds crazy, but passing too many things is not actually a problem in ObjC.
@interface MyObject: NSObject
- (void)something;
@end
...
MyObject *object = [MyObject new];
[object performSelector:@selector(something) withObject:@"thing"];
Works fine. What!?!? Yeah. This is actually how your button example works. Put a breakpoint in your IBAction
and go look at the caller:
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
It always calls performSelector:withObject:withObject:
and passes you the sender and the event even if you didn't ask for them. Try looking at $rdx
(arg2) and $rcx
(arg3) if you don't believe me (assuming you're in the Simulator. If you want all the right registers, you'll want the list). From my (IBAction)doThing
method:
(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}
)}
So yes, you can tear apart the selector and figure out what to send or not send, but the typical way to do this in Cocoa is just set things up so you can always send everything and let the receiver ignore it.