【问题标题】:Check if on ObjC selector (SEL) has a parameter检查 ObjC 选择器 (SEL) 上是否有参数
【发布时间】:2017-06-26 01:30:36
【问题描述】:

就像UIButton 我可以:

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

或者:

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

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

【问题讨论】:

    标签: objective-c selector introspection nsinvocation


    【解决方案1】:

    首先,我会使用NSStringFromSelector()。如果由于某种原因给您带来问题,我会使用sel_getName

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

    【讨论】:

      【解决方案2】:

      Jeffery 是对的(许多动态 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)(假设您在模拟器中。如果您想要所有正确的寄存器,您会想要 the list)。来自我的(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 中执行此操作的典型方法只是进行设置,以便您始终可以发送所有内容并让接收者忽略它。

      【讨论】:

      • 这是C的行为,是不是“疯了”?
      • @AminNegm-Awad 一点也不疯狂。那些不熟悉类 C 语言的低级调用约定的人可能会感到惊讶(这在语言中并非普遍适用)。当然,同样的方法可以(并且正在)用于间接调用 C 函数指针。
      • @AminNegm-Awad:我不认为这实际上是在 C 中定义的。我相信尝试使用不同签名的函数指针调用函数(即调用不使用参数的函数)带参数的函数指针类型)在 C 中是技术上未定义的行为。他们在这里所做的只是恰好适用于 Apple 架构(可能还有大多数其他架构)。
      • @newacct 我没有用标准检查它。但是实现没有原型。在这种情况下,如果我错了,请纠正我,传递太多 args 不是 UB。这是因为它只能中断,如果第一个 args 到函数 params 的传递方式取决于 params 的总数。但这根本行不通,因为没有原型,参数的数量是未知的。我认为,RTE 只是从定义的行为中获取优势。但也许我错了。
      • @AminNegm-Awad:我不确定您所说的“实现没有原型”是什么意思
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-20
      • 2012-02-24
      • 1970-01-01
      • 1970-01-01
      • 2011-02-16
      • 2017-05-03
      • 2011-02-09
      相关资源
      最近更新 更多