了解Objective-C范围问题

pix*_*eak 5 iphone objective-c ipad ios

以下是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];
}
Run Code Online (Sandbox Code Playgroud)

我没有myAwesomeMethod在我的头文件中声明的方法,但为什么它,我可以打电话myAwesomeMethodviewDidLoad,但不是在myOtherAwesomeMethod

我知道这个错误的解决方案是在我的头文件中声明方法,但我想了解为什么会发生这种情况.

ben*_*ado 11

在C中,规则是:您必须在使用它之前声明它.

文件从上到下编译.所以这是你的代码中发生的事情:

  1. 编译器读取您的类的@interface声明.既然你说在头文件中既没有声明方法,也没有在符号表中.

    您的符号表包含:什么都没有

  2. 编译器读取方法定义myAwesomeMethod.您尚未声明它,因此它会添加到符号表中.该方法的主体包含一个调用NSLog,很久以前就是在Apple提供给你的标题中声明的.

    您的符号表包含: myAwesomeMethod

  3. 编译器读取方法定义viewDidLoad; 它实际上是在超类的头文件中声明的.该方法的主体包含一个调用myAwesomeMethod,被发现!它还包含一个调用myOtherAwesomeMethod.从来没有听说过!

    现在,这不是错误,因为它仍然可以生成代码来进行调用.它可以通过你如何使用它来推断参数类型(在这种情况下,没有参数).但是不能确定返回类型(仅仅因为你没有使用返回值并不意味着没有返回值).所以它继续假设调用返回id并生成警告.

    您的符号表包含: myAwesomeMethod

  4. 最后,编译器读取方法定义myOtherAwesomeMethod.它被添加到符号表中.它编译包含调用的主体,该主体myAwesomeMethod位于表中.一切都很顺利.

    在文件的末尾,符号表包含:myAwesomeMethod,myOtherAwesomeMethod

如果你来自像Java这样的语言,这可能看起来很愚蠢,但那是因为Java的工作方式与C不同.在Java中,源代码是一步编译和链接的; 如果没有可用的源代码或类文件,则无法编译引用另一个类的类.这可能很麻烦,但另一方面,除了定义之外,你永远不需要声明.

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

在Objective-C中,链接器实际上并不知道/关心Objective-C消息,因为在编译时/链接时无法知道对象是否可以响应消息.因此它将降压传递给运行时,如果在没有方法定义的情况下达到这个目的,则会抛出异常.