2009-09-19 19 views
23

En primer lugar, perdone la estupidez de esta pregunta, pero no soy de un fondo de C/C++. No estoy muy claro cuál es la diferencia en los roles entre los archivos .h y .m cuando se trata de propiedades.Confusión con encabezado y archivos de implementación en Objective-C

entiendo el concepto de interfaces, y veo que, en parte, el archivo .h es una interfaz de la aplicación, pero lo que no tengo claro es lo siguiente:

  • Por qué se definen propiedades/métodos fuera de {} llaves?
  • ¿qué estoy definiendo en los tirantes cuando escribo algo como esto:

    IBOutlet UITextField * NumberField;

    ¿Es esta una definición de campo en una interfaz?

  • Cuando agrego las líneas de @Property a los archivos .h, ¿estas implementaciones reales de una propiedad automática de n o solo un plano de interfaz? Si es así, ¿la @syntesis es la implementación real?

Creo que mi mayor confusión parece ser que si quiero una propiedad que estoy definiendo lo que necesito en tres lugares diferentes (1) en las interfaces de los apoyos, (2) como @property fuera de las llaves y (3) con @synthesis en el archivo .m. Esto parece largo aliento, pero está bien si puedo averiguar qué hacen estas tres partes.

Cheers, Chris.

Respuesta

75

voy a responder a sus preguntas más adelante, pero tal vez la mejor manera de aprender estas cosas es leer algunas notas fáciles de usar destinados a personas nuevas a la lengua, como the Learn Objective-C tutorial encima en cocoadevcentral.

Un ejemplo

Me gustaría ayudar a responder a sus preguntas con un ejemplo (Me encanta aprender con el ejemplo). Digamos que eres un profesor que escribe un programa que les pregunta a los alumnos una pregunta específica de sí/no, y realiza un seguimiento de cuántos lo hacen correctamente y cuántos alumnos ha solicitado.

Aquí es una posible interfaz para esta clase:

@interface Question : NSObject { 
    NSString* questionStr; 
    int numTimesAsked; 
    int numCorrectAnswers; 
} 

@property (nonatomic, retain) NSString* questionStr; 
@property (nonatomic, readonly) int numTimesAsked; 
@property (nonatomic) int numCorrectAnswers; 
@property (nonatomic) int numWrongAnswers; 

- addAnswerWithTruthValue: (BOOL) isCorrect; 
@end 

Las tres variables dentro de las llaves son variables de instancia, y cada instancia de la clase tendrán sus propios valores para cada una de esas variables. Todo lo que está fuera de las llaves pero antes de @end es una declaración de un método (incluidas las declaraciones @property).

(Nota al margen:. Para muchos objetos, es útil tener retain propiedades, ya que desea evitar la sobrecarga de copiar el objeto, y asegúrese de que no se libera mientras se está usando Es legal retain un NSString como en este ejemplo, pero it is often considered good practice to use copy instead of retain ya que un NSString* en realidad podría apuntar a un objeto NSMutableString, que más tarde puede cambiar cuando el código de espera que se mantenga el mismo.)

lo que hace @property

Cuando se declara una @property, que está haciendo dos cosas:

  1. Declarar un método setter y getter en la interfaz de la clase, y
  2. que indican cómo el setter y getter se comportan.

Para el primero, es suficiente para saber que esta línea:

@property (nonatomic, retain) NSString* questionStr; 

es básicamente el mismo que esto:

- (NSString*) questionStr;       // getter 
- (void) setQuestionStr: (NSString) newQuestionStr; // setter 

en la cabecera. Literalmente estás declarando esos dos métodos; puede llamarlos directamente o usar la notación de puntos como un atajo para llamarlos por usted.

La parte "básicamente" en "básicamente lo mismo" es la información adicional proporcionada por palabras clave como nonatomic y retain.

La palabra clave nonatomic indica que no son necesariamente seguros para subprocesos.La palabra clave retain común indica que el objeto conserva cualquier valor establecido y libera los valores anteriores a medida que se liberan.

Por ejemplo:

// The correct answer to both questions is objectively YES. 
Question* myQuestion = [[Question alloc] init]; 
NSString* question1 = [[NSString alloc] initWithString:@"Is pizza tasty?"]; 
// question1 has retain count of 1, from the call to alloc 
myQuestion.questionStr = question1; 
// question1 now has a retain count of 2 
NSString* question2 = [[NSString alloc] initWithString:@"Free iPhone?"]; 
myQuestion.questionStr = question2; 
// question1 has a retain count of 1, and question2 has retain count of 2 

Si la declaración de @propertyquestionStr había sido assign lugar, a continuación, todas las declaraciones myQuestion.questionStr = no habría hecho ningún cambio en absoluto a los recuentos de retener.

Puede read a little more about properties here.

Qué hacer IBOutlet y IBAction

Estos son, básicamente, las palabras no-op que actúan sólo como una manera de contar Interface Builder que las piezas del archivo de cabecera que presten atención. IBOutlet se convierte literalmente en una cadena vacía cuando el compilador la examina, y IBAction se convierte en el valor de retorno void. Sin embargo, los necesitamos para trabajar con Interface Builder, por lo que son importantes, solo que no para el compilador.

Nota rápida sobre estructuras C y flecha vs notación de puntos

Por cierto, la parte de datos de un objeto de Objective-C es muy similar a una estructura C. Si usted tiene un puntero a una estructura C, puede utilizar la flecha notación -> para referirse a una parte específica de la estructura, así:

struct MyStructType { 
    int i; 
    BOOL b; 
}; 
struct MyStructType* myStruct; 
myStruct->i = 3; 
myStruct->b = TRUE; // or YES in Objective-C. 

Esta misma sintaxis funciona de la misma manera en Objective-C:

Question* question = [[Question alloc] init]; 
question->questionStr = @"Is this a long answer?"; // YES 

Pero al hacer esto, no hay ninguna llamada método sucediendo detrás de las escenas, a diferencia de la notación de punto. Con la notación de puntos, que está llamando la incubadora (o getter si no hay = después), y estas dos líneas es el mismo:

question.questionStr = @"Chocolate?"; 
[question setQuestionStr:@"Chocolate?"]; 

A menudo es una buena idea para evitar la notación flecha en favor de la la notación de puntos, ya que la notación de puntos le permite aplicar un estado válido; por ejemplo, que los punteros de su clase siempre se conservan. Incluso puede prohibir que otros utilicen la notación de flecha al declarar las variables de instancia como @private; aún pueden usar el getter y el setter para acceder a él, si declara @property para ello.

Lo que hace @synthesize

Ahora, cuando llegue a su alrededor para la aplicación real de su clase, @synthesize dice algo así como "asegúrese de que el getter y setter son implementadas para esta propiedad." Hace no diga "implementar ambos para mí", porque el compilador es lo suficientemente educado como para verificar primero su propia implementación y solo completar las piezas que ha perdido. No tiene que usar @synthesize en absoluto, incluso si usa @property fuera del wazoo; siempre podría proporcionar sus implementaciones para sus instaladores y buscadores, si le gusta ese tipo de cosas.

Usted probablemente ha notado en la interfaz Question por encima de que hay una propiedad que es no una variable de instancia (numWrongAnswers), lo cual está bien porque estás declarando métodos. En el código de ejemplo aquí, se puede ver cómo funciona realmente:

@implementation Question 

@synthesize questionStr, numTimesAsked, numCorrectAnswers; 

- (void) setNumCorrectAnswers: (int) newCorrectAnswers { 
    // We assume the # increases, and represents new answers. 
    int numNew = newCorrectAnswers - numCorrectAnswers; 
    numTimesAsked += numNew; 
    numCorrectAnswers = newCorrectAnswers; 
} 

- (int) numWrongAnswers { 
    return numTimesAsked - numCorrectAnswers; 
} 

- (void) setNumWrongAnswers: (int) newWrongAnswers { 
    int numNew = newWrongAnswers - self.numWrongAnswers; 
    numTimesAsked += numNew; 
} 

- (void) addAnswerWithTruthValue: (BOOL) isCorrect { 
    if (isCorrect) { 
    self.numCorrectAnswers++; 
    } else { 
    self.numWrongAnswers++; 
    } 
} 

@end 

Una cosa que está pasando aquí es que estamos fingiendo una variable de instancia llamada numWrongAnswers, lo que sería la información redundante si se guardó en la clase. Como conocemos numWrongAnswers + numCorrectAnswers = numTimesAsked en todo momento, solo necesitamos almacenar dos de estos tres puntos de datos, y siempre podemos pensar en términos del otro utilizando los dos valores que conocemos. El punto aquí es comprender que una declaración @property realmente se trata de declarar un método setter y getter, que generalmente corresponde a una variable de instancia real, pero no siempre. La palabra clave @synthesize de forma predeterminada hace que corresponda a una variable de instancia real, de modo que sea fácil para el compilador completar la implementación por usted.

Razones para tener separados .h y .m archivos

Por cierto, todo el punto de métodos que declaran en un solo archivo (el archivo .h cabecera) y la definición de su ejecución en otro (el archivo .m o métodos) es ayudar a desacoplar el código. Por ejemplo, si solo actualiza un archivo .m en su proyecto, no tiene que volver a compilar los otros archivos .m, ya que su código objeto permanecerá igual, esto ahorra tiempo. Otra ventaja es que puede usar una biblioteca que solo incluye archivos de encabezado y código de objeto precompilado, o incluso bibliotecas dinámicas donde necesita el archivo de encabezado para que el compilador sepa qué métodos existen, pero esos métodos ni siquiera están vinculados en con tu archivo ejecutable. Estas ventajas son difíciles de apreciar la primera vez que se comienza a codificar, pero solo el desglose lógico y la encapsulación de la implementación se vuelven útiles después de un tiempo breve.

¡Espero que sea útil!

+1

Algunas críticas constructivas de una publicación impresionante en general: (1) Las propiedades de NSString realmente deberían marcarse como retener, (2) Creo que no se necesita el bit sobre por qué quieres los archivos .m y .h en este contexto, ya que es mejor seguir con lo que está pasando. Me gustó que hablaste sobre el propósito de IBOutlet e IBAction. –

+3

En realidad, las propiedades de NSString deben marcarse como copia. – bbum

+0

Vaya, eso es lo que quise decir, el código de ejemplo ya lo tiene como retener ...gracias por corregir mi error! –

1
  1. métodos se definen fuera de las llaves desde los tirantes están destinados para encapsular el estado del objeto que se puede argumentar no incluye los métodos de instancia o de clase.

  2. Lo que se está definiendo en los tirantes son variables de instancia que pueden ser referenciados como self.ivar

  3. Los @property y @synthesize directivas simplemente descriptores de acceso de configuración para usted variables de instancia para que pueda establecer ellos haciendo auto .ivar = someVar. Entonces, en otras palabras, configura la "sintaxis de punto" para que la use.

y para responder a su pregunta final: Para definir una variable de propiedad o instancia simplemente declarar en su archivo .h como una variable dentro de las llaves. Para configurar los métodos de acceso en esa misma propiedad, necesita hacer TANTO @property y @synthesize.

+0

en realidad, se accede a las variables de instancia como self-> ivar. solo se accede a las propiedades usando self.ivar – newacct

0
  1. Bueno, eso es solo la sintaxis de Objective C, los métodos y @property outside {} y las variables dentro de {}.

  2. @property es la forma de decir que va a escribir getter y setters (como aplicarlo), pero puede escribir getter/setter sin configurar @property. @property está en archivo .h porque es una declaración. Y por qué está afuera {}, así como dije antes es solo la sintaxis, ¿qué podemos hacer?

  3. @synthesis utilizará getter y setters de implementación real, si no realiza la síntesis pero los ha establecido en @property, debe implementar esos getter y setters con su mano. Y @synthesis está en el archivo .m porque es su implementación.

Algo más para que lea sobre este tema puede encontrarlo aquí.

http://theocacao.com/document.page/510

0

Las variables dentro de los paréntesis definen la estructura física de su clase. Esas son las variables de instancia reales que almacenan información.

Las cosas que están fuera de los corchetes conforman la interfaz de la clase: métodos y propiedades. Una propiedad en sí misma no reserva ningún espacio de almacenamiento ni afecta ninguna variable; solo declara una interfaz genérica para acceder a algo. Recuerde que una propiedad no tiene que tener una variable de instancia subyacente; por ejemplo, la propiedad totalPrice en una clase ShoppingCart puede sumar dinámicamente los precios de todos los artículos en el carro.

Dentro del archivo de implementación, le dice a la clase cómo hacer su trabajo. Para los métodos, obviamente, solo proporciona una implementación. Para una propiedad, puede proporcionar implementaciones de acceso o pedirle que sintetice accesadores para una variable de instancia.

Cuestiones relacionadas