Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding Objective-C scope issue

Following is a snippet from a View Controller's implementation:

- (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];
}

I do not have myAwesomeMethod method declared in my header file, but why is it that I can call myAwesomeMethod in viewDidLoad, but not in myOtherAwesomeMethod?

I know the solution to this error is to declare the method in my header file, but I'd like to understand why this is happening.

like image 591
pixelfreak Avatar asked Jul 20 '11 00:07

pixelfreak


1 Answers

In C, the rule is: you got to declare it before you use it.

Files are compiled from the top down. So here's what is happening in your code:

  1. The compiler reads the @interface declaration for your class. Since you say neither method is declared in the header file, neither are in the symbol table yet.

    Your symbol table contains: nothing yet

  2. The compiler reads the method definition for myAwesomeMethod. You haven't declared it yet, so it gets added to the symbol table. The body of the method contains a call to NSLog, which was declared long ago in a header provided to you by Apple.

    Your symbol table contains: myAwesomeMethod

  3. The compiler reads the method definition for viewDidLoad; it was actually declared in a superclass's header file. The body of the method contains a call to myAwesomeMethod, which was found! It also contains a call to myOtherAwesomeMethod. Never heard of it!

    Now, this isn't an error, because it can still generate the code to make the call. It can infer argument types by how you use it (in this case, no arguments). It can't be sure of the return type, though (just because you don't use the return value doesn't mean there wasn't one). So it proceeds on the assumption that the call returns id and generates a warning.

    Your symbol table contains: myAwesomeMethod

  4. Finally, the compiler reads the method definition for myOtherAwesomeMethod. It gets added to the symbol table. It compiles the body, which contains a call to myAwesomeMethod, which is in the table. All's well that ends well.

    At the end of the file, your symbol table contains: myAwesomeMethod, myOtherAwesomeMethod

If you're coming from a language like Java, this may seem silly, but that's because Java works differently than C. In Java, source code is compiled and linked in one step; you can't compile a class that refers to another class without having either the source code or class files available. That can be a hassle, but on the other hand, you never need about declarations apart from definitions.

In C, compiling and linking are different steps. The compiler just generates object code that refers to symbols that may be defined elsewhere; it uses declarations to make sure it generates the right code. The linker is responsible for matching up all those symbols with definitions in other libraries, static or dynamic.

In Objective-C, the linker doesn't actually know/care about Objective-C messages, because there is no way of knowing at compile-time/link-time whether an object can respond to a message. So it passes the buck to the runtime, which throws an exception if you get that far without a method definition.

like image 110
benzado Avatar answered Sep 28 '22 00:09

benzado