【问题标题】:Multiple parts of methods in Objective CObjective C 中方法的多个部分
【发布时间】:2011-11-01 11:07:06
【问题描述】:

我正在学习 Objective C,并在阅读方法时注意到了这个古怪的怪癖。

与 Java 和 C++ 一样,Obj.C 可以接受多个参数,这很好,但是它指出目标 C 方法可以有多个名称,这似乎不太适合我。

例如:

-(NSArray *)shipsAtPoint:(CGPoint)bombLocation withDamage:(BOOL)damaged;

在上面的示例中,有两个参数,bombLocation(返回类型 CGPoint)和损坏(返回类型 BOOL),并且在方法名称旁边似乎拆分为 shipatpoint:withDamage

我不明白这是怎么回事... 当它声明一个方法可以有多个名称时,它意味着什么? 这是否仅适用于需要多个参数的方法?或者,假设我想用一个名称命名我的方法但为其提供多个参数,这是可能的还是我必须为它提供多个名称,每个名称都对应一个参数?如果是,那为什么?

感谢您解决我的困惑!!! :)

【问题讨论】:

  • 您确定是多个名称,而不是参数或其他什么?这也让我感到困惑,因为我几乎可以肯定方法仅由一个名称定义。
  • nawww...它们显然是用多个名称定义的,

标签: iphone objective-c ios xcode


【解决方案1】:

Objective-C 使用基于选择器的消息传递系统。这与方法调用不太一样。当你看到这样的代码时:

[world shipsAtPoint:point withDamage:YES];

这被转换为以下 C 调用(在最常见的情况下):

objc_msgSend(world, @selector(shipsAtPoint:withDamage:), point, YES);

@selector() 构造返回一个唯一标识符。该标识符的确切格式是内部实现细节。

objc_msgSend 在它的几十个字节的汇编程序中包含了相当多的逻辑。但在最简单的情况下,它会查找 world 的类,遍历选择器表,直到找到与 shipsAtPoint:withDamage: 匹配的选择器,然后在该槽中获取函数指针。然后它跳转到该函数指针,只留下其余的参数(在寄存器中或在堆栈中,以适合处理器)。该位置的函数是您的方法,它根据您的声明知道其参数的顺序和类型。

对你来说重要的是选择器是shipsAtPoint:withDamage:。这通常是方法的唯一名称。没有您建议的“多个名称”。 (通常......Objective-C 运行时非常强大,可以将多个选择器指向同一个实现。)

正如 Joe 指出的那样,选择器可以采用 foo:: 的形式。这将表示一个带有两个参数的方法,并且会像[world foo:point :YES] 一样调用。你不应该这样做。阅读起来令人难以置信的混乱。但这是合法的。

【讨论】:

  • 如果你真的想要,你可以使用 Objective-C 运行时调用每个方法! ಠ_ಠ
  • 你可以,但它比听起来要难一些。如果您的方法返回浮点或结构,则必须使用不同版本的objc_msgSend。如果您要发送消息super,则需要调用一个特殊版本。编译器会为您处理所有这些边缘情况。如果您使用 ARC,它也会更快。目前 LLVM 似乎希望在来自 IMP 的返回值上包含额外的保留/释放,即使底层方法是 void。他们将来可能会解决这个问题。
【解决方案2】:

原因是为了更容易理解。

以您的示例为例,该方法在 C++ 中将是这样的:

 int shipsAtPointWithDamage (CGPoint bomb, BOOL damage)  //I don't really know C++ 

好的,所以第一个参数是船的点,伤害是第二个。很容易弄清楚,但就是这样,你必须弄清楚,你必须看看方法来尝试弄清楚每件事是什么。

在 Objective-C 中你有

-(NSArray *)shipsAtPoint:(CGPoint)bombLocation withDamage:(BOOL)damaged;

每个参数都有明确的定义,第一个是船的点,第二个是伤害。 它读起来像一个句子,而对于 C++(以及几乎所有其他语言)则不是。

如果你想让一个方法在 Obj-C 中有多个参数,你必须这样写:

-(returnType)paraOne:(type*)name paraTwo:(type*)name

这只是需要习惯的东西,每种语言都不同。一旦你习惯了 Objective-C 做事的方式,你会觉得它绝对很棒。

编辑:正如 filipe 指出的那样,因为方法作为多个参数并不意味着它有多个名称,在我上面给出的示例中,方法名称将是 paraOne:paraTwo,而不是 paraOne:

【讨论】:

  • 只是为了补充这个答案,这并不意味着该方法有多个名称,只是: 是名称的一部分。所以在那个例子中,方法的名字不是简单的shipsAtPoint,方法的全名是shipsAtPoint:withDamage:
  • 所以,马特,如果我在这里错了,请纠正我,方法名称被分成多个名称以方便用户阅读,它们并没有真正服务于任何编译器目的或提供来自其他语言的方法的独特功能比如C++和Java就不行?
  • @Parijat 据我了解,这只是一个提高可读性的功能。我不是 100% 确定,因为我不是专家,但苹果鼓励开发人员这样做。他们在 Objective-C 语言文档中特意说你可以做类似-(returnType)paraOne:(type*)name :(type*)name 的事情,但他们明确表示这是不好的风格
【解决方案3】:

我认为你很困惑。一个方法不能有多个名称,但参数在标头中的名称可能与它们在实现中的名称不同。

该方法的名称是shipsAtPoint:withDamage:。这也称为selector

此方法返回NSArray 的实例,并接受CGPoint 作为第一个参数,BOOL 作为第二个参数。

但是,参数的名称可能不同。这是完全有效的:

// .h file
-(NSArray *)shipsAtPoint:(CGPoint)bombLocation withDamage:(BOOL)damaged;

// .m file
-(NSArray *)shipsAtPoint:(CGPoint)loc withDamage:(BOOL)dmg {
  // ...
}

最后,ObjC 主要是一些不错的语法糖。您应该知道,任何方法调用实际上都归结为一些看起来或多或少像这样的 C:

objc_msgSend(receiverObj, @selector(shipsAtPoint:withDamage:), point, damage);

所以在一天结束时,你有一个接收器、一个选择器和你的参数。但是 ObjC 的语法比这要好得多。

【讨论】:

    【解决方案4】:

    可以提供没有标记参数的方法,但显然不鼓励。

    -(void)badmethod:(id)obj1:(id)obj2:(id)obj3
    {
    
    }
    
    //...
    //Usage
    [self badmethod:nil :nil :nil];
    
    SEL sel = @selector(badmethod:::);
    

    【讨论】:

      【解决方案5】:

      Here 是我见过的最好的解释。它包括与 C++/C 的比较以及许多其他有用的信息。

      【讨论】:

      • 杰西,我以前读过那篇文章,但不知怎的,我从来没有点击过它,无论如何我会再读一遍,但如果你有别的东西,那就太棒了!
      • 您不能使用多个名称定义 1 个方法。此外,objective-c 中没有“覆盖”。参数名称被认为是该方法的“一部分”,这是我能解释它的最佳方式。所以addObject:(id)fooaddObject:(id)foo withBar:(id)bar 是完全不同的方法
      • 是的,但是 addObject:(id)foo withBar:(id)bar 也和 addObjectwithBar:(id)foo:(id)bar 一样;
      猜你喜欢
      • 1970-01-01
      • 2010-12-14
      • 2011-05-27
      • 2012-08-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-24
      • 1970-01-01
      相关资源
      最近更新 更多