2011-07-20 19 views
5

siguiente es un fragmento de la aplicación de una vista de Controlador:entendimiento de Objective-C cuestión alcance

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

no tengo myAwesomeMethod método declarado en mi archivo de cabecera, pero ¿por qué es que puedo llamar myAwesomeMethod en viewDidLoad, pero no en myOtherAwesomeMethod?

Sé que la solución a este error es declarar el método en mi archivo de encabezado, pero me gustaría entender por qué sucede esto.

+0

¿dónde llamas '-myOtherAwesomeMethod'? – vikingosegundo

+1

Creo que es solo una advertencia, no un error? – tia

+0

También puede simplemente NSLogar la cadena, sin necesidad de formatearla. –

Respuesta

11

En C, la regla es: tienes que declararlo antes de usarlo.

Los archivos se compilan de arriba hacia abajo. Así que aquí es lo que está sucediendo en el código:

  1. El compilador lee la declaración @interface para su clase. Como dices que ninguno de los métodos está declarado en el archivo de encabezado, ninguno está en la tabla de símbolos todavía.

    Su tabla de símbolos contiene: nada todavía

  2. El compilador lee la definición del método para myAwesomeMethod. Aún no lo ha declarado, por lo que se agrega a la tabla de símbolos. El cuerpo del método contiene una llamada al NSLog, que fue declarada hace mucho tiempo en un encabezado provisto por Apple.

    Su tabla de símbolos contiene: myAwesomeMethod

  3. El compilador lee la definición del método para viewDidLoad; en realidad fue declarado en el archivo de encabezado de una superclase. El cuerpo del método contiene una llamada al myAwesomeMethod, ¡que se encontró! También contiene una llamada al myOtherAwesomeMethod. ¡Nunca lo oí!

    Ahora, esto no es un error, porque aún puede generar el código para realizar la llamada. Puede inferir tipos de argumentos según cómo lo use (en este caso, sin argumentos). Sin embargo, no puede estar seguro del tipo de devolución (solo porque no utilice el valor devuelto no significa que no haya ninguno). Por lo tanto, se parte de la suposición de que la llamada devuelve id y genera una advertencia.

    Su tabla de símbolos contiene: myAwesomeMethod

  4. Por último, el compilador lee la definición del método para myOtherAwesomeMethod. Se agrega a la tabla de símbolos. Compila el cuerpo, que contiene una llamada al myAwesomeMethod, que está en la tabla. Bien está lo que bien acaba.

    Al final del archivo, su tabla de símbolos contiene: myAwesomeMethod, myOtherAwesomeMethod

Si usted viene de un lenguaje como Java, esto puede parecer tonto, pero eso es porque Java no funciona igual que C En Java, el código fuente se compila y se vincula en un solo paso; no puede compilar una clase que se refiera a otra clase sin tener el código fuente o los archivos de clase disponibles. Eso puede ser una molestia, pero, por otro lado, nunca necesitará declaraciones aparte de las definiciones.

En C, la compilación y el enlace son pasos diferentes.El compilador solo genera código de objeto que hace referencia a símbolos que pueden definirse en otra parte; usa declaraciones para asegurarse de que genera el código correcto. El vinculador es responsable de hacer coincidir todos los símbolos con definiciones en otras bibliotecas, estáticas o dinámicas.

En Objective-C, el vinculador no sabe realmente ni se preocupa por los mensajes de Objective-C, porque no hay forma de saber en tiempo de compilación/tiempo de enlace si un objeto puede responder a un mensaje. Por lo tanto, pasa el dinero al tiempo de ejecución, lo que arroja una excepción si llega tan lejos sin una definición de método.

+0

Gracias @benzado, descubrí que el orden es el culpable. Realmente no necesito declararlo en ninguna parte, pero la definición del método debe venir antes de que se llame. Estoy aceptando tu respuesta de todos modos por la increíble explicación. – pixelfreak

0

Admito que no estoy seguro de por qué funciona en viewDidLoad.

Observe que el error dice "no DECLARÓ" un método. Esa es una pista de que está buscando una declaración (típicamente en un encabezado) ... es decir, un prototipo de función.

Sin embargo, no hay nada que diga que una declaración debe estar en un encabezado. Puede ponerlo en la parte superior del archivo de implementación si no va a llamar a ese método desde fuera del archivo.