以下是View Controller的实现的摘录:

- (void)myOtherAwesomeMethod
{
    [self myAwesomeMethod]; // Compile ERROR here: Receiver type for instance message does not declare a method with selector
}

- (void)myAwesomeMethod
{
    NSLog(@"%@", @"Calling my awesome method...");
}

- (void)viewDidLoad
{
    [self myAwesomeMethod];

    [self myOtherAwesomeMethod];
}

我没有在头文件中声明myAwesomeMethod方法,但是为什么可以在myAwesomeMethod中调用viewDidLoad而不在myOtherAwesomeMethod中调用ojit_code呢?

我知道解决此错误的方法是在我的头文件中声明该方法,但我想了解为什么会这样。

最佳答案

在C语言中,规则是:必须在使用它之前声明它。

文件是从上到下编译的。因此,这就是您的代码中正在发生的事情:

  • 编译器为您的类读取@interface声明。由于您说头文件中没有声明任何方法,因此符号表中也没有声明方法。

    您的符号表包含:尚无
  • 编译器读取myAwesomeMethod的方法定义。您尚未声明它,因此它已添加到符号表中。该方法的主体包含对NSLog的调用,该调用早在Apple提供给您的 header 中就已声明。

    您的符号表包含:myAwesomeMethod
  • 编译器读取viewDidLoad的方法定义;它实际上是在父类(super class)的头文件中声明的。方法的主体包含对myAwesomeMethod的调用,该调用已找到!它还包含对myOtherAwesomeMethod的调用。从来没有听说过!

    现在,这不是错误,因为它仍然可以生成代码来进行调用。它可以根据您的用法来推断参数类型(在这种情况下,没有参数)。但是,不能确定返回类型(只是因为您不使用返回值并不意味着没有返回值)。因此,它在调用返回id并生成警告的假设下进行。

    您的符号表包含:myAwesomeMethod
  • 最后,编译器读取myOtherAwesomeMethod的方法定义。它被添加到符号表中。它编译主体,该主体包含对表中myAwesomeMethod的调用。一切顺利,一切顺利。

    在文件末尾,您的符号表包含:myAwesomeMethodmyOtherAwesomeMethod

  • 如果您来自Java之类的语言,这似乎很愚蠢,但这是因为Java与C的工作方式不同。如果没有源代码或类文件,则不能编译引用另一个类的类。这可能很麻烦,但是另一方面,除了定义之外,您不需要声明。

    在C语言中,编译和链接是不同的步骤。编译器只生成引用可能在其他地方定义的符号的目标代码。它使用声明来确保它生成正确的代码。链接器负责将所有这些符号与其他库(静态或动态)中的定义进行匹配。

    在Objective-C中,链接器实际上并不了解/关心Objective-C消息,因为无法在编译时/链接时知道对象是否可以响应消息。因此,它会将责任传递给了运行时,如果您在没有方法定义的情况下走得那么远,则会抛出异常。

    09-18 02:49