¿Cómo funciona un guión bajo frente a una variable en una clase de cacao objetivo-c?


157

He visto en algunos ejemplos de iPhone que los atributos han usado un guión bajo _ delante de la variable. Alguien sabe que significa esto? O como funciona?

Un archivo de interfaz que estoy usando se ve así:

@interface MissionCell : UITableViewCell {
    Mission *_mission;
    UILabel *_missionName;
}

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

No estoy seguro exactamente de lo que hace lo anterior, pero cuando trato de configurar el nombre de la misión como:

aMission.missionName = missionName;

Me sale el error:

solicitud de miembro 'missionName' en algo que no sea una estructura o unión

Respuestas:


97

Si usa el prefijo de subrayado para sus ivars (que no es más que una convención común, pero útil), entonces debe hacer 1 cosa adicional para que el descriptor de acceso automático (para la propiedad) sepa qué ivar usar. Específicamente, en su archivo de implementación, synthesizedebería verse así:

@synthesize missionName = _missionName;

Más genéricamente, esto es:

@synthesize propertyName = _ivarName;

78
con propiedades de auto-síntesis esto ya no es necesario. Xcode sintetiza un @property xxxx con un ivar llamado _xxxx detrás de escena. Ordenado.
LearnCocos2D

@ LearnCocos2D ¡Hola! Un novato en iOS aquí y hay algo que necesito aclarar. Por todo este tiempo lo que hice fue declarar el propertyen el archivo .h y en el .m fie acceder a ella usando selfcomo tal, self.someProperty. ¿Es este el camino correcto? ¿O debería estar usando los ivars en el código?
Isuru

configurar el ivar no ejecuta el configurador de propiedades: usted decide si es una buena idea o no para cada caso particular
LearnCocos2D

Pregunta novata: ¿por qué no usar los ivars directamente? ¿Por qué debería declarar una var separada para contener el ivar?
Allen

1
@Allen, si entiendo su pregunta correctamente: la var separada que está declarando es un puntero a la variable real. Esto es importante por algunas razones (que yo sepa) En primer lugar, cuando pasa un puntero a una función, no está duplicando su valor. Simplemente le está diciendo a la función dónde encontrar el valor a utilizar. Esto ayuda a mantener baja la memoria utilizada (y también ayuda con la asignación y el reparto de memoria, lo cual es importante en ausencia de la 'recolección de basura' que encontrará en Java)
David Sigley

18

Es solo una convención de legibilidad, no hace nada especial para el compilador. Verá que las personas lo usan en variables de instancias privadas y nombres de métodos. En realidad, Apple recomienda no usar el guión bajo (si no tiene cuidado podría anular algo en su superclase), pero no debería sentirse mal por ignorar ese consejo. :)


19
Por lo que entiendo, Apple recomienda no usar el prefijo de subrayado en los nombres de los métodos (se reservan eso como una convención para los métodos privados), pero no tienen ninguna recomendación sobre los nombres de variables de instancia.
Kelan

9
@Kelan De hecho, Apple alienta a hacerlo : "Por lo general, no debe acceder a las variables de instancia directamente, sino que debe usar métodos de acceso (sí accede a las variables de instancia directamente en los métodos init y dealloc). Para ayudar a señalar esto, prefija la instancia nombres de variables con un guión bajo (_), por ejemplo: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov

En realidad, no creo que Apple nos aliente a hacerlo, ya que todos sus propios códigos de muestra en la Biblioteca para desarrolladores de iOS no tienen el ( ) en ellos. Apple también dice que lo han reservado, lo que debe significar que lo usan internamente para sus propios marcos como UIKit, etc. Es por eso que no debemos usarlo descuidadamente. Pero veo eso, en el enlace que proporcionó @kelan. De hecho, dicen en el "historial de revisiones" que es "adecuado" usar ( ). Interpreto que es como "podemos" usarlo si queremos.
WYS

La documentación de Apple que dice no utilizar el prefijo de subrayado para los nombres de los métodos está aquí .
ThomasW

9

El único propósito útil que he visto es diferenciar entre variables locales y variables miembro como se indicó anteriormente, pero no es una convención necesaria. Cuando se combina con una @property, aumenta la verbosidad de las declaraciones de sintetización:@synthesize missionName = _missionName; , y es feo en todas partes.

En lugar de usar el guión bajo, solo use nombres de variables descriptivas dentro de métodos que no entren en conflicto. Cuando deben entrar en conflicto, el nombre de la variable dentro del método debe sufrir un guión bajo, no la variable miembro que pueden usar varios métodos . El único lugar común en el que esto es útil es en un setter o en un método init. Además, hará que la declaración @synthesize sea más concisa.

-(void)setMyString:(NSString*)_myString
{
    myString = _myString;
}

Editar: con la última función de compilación de auto-síntesis, ahora uso guión bajo para el ivar (en la rara ocasión en que necesito usar un ivar para que coincida con lo que hace la auto-síntesis.


Es al revés. La variable privada está subrayada. La propiedad no. y cuando los sintetizas, los unes.
Justin

Eso es exactamente como lo describo, excepto que lo llamé una "variable miembro" en lugar de una "variable privada".
Peter DeWeese

¡Ay! Esto es un problema ... la síntesis automática hará que ivar _myString, lo que significa que su setter no funcionará (porque no podrá distinguir su ivar del parámetro del método).
geowar

Correcto, por eso agregué la edición al final cuando Apple agregó la síntesis automática.
Peter DeWeese

5

Realmente no significa nada, es solo una convención que algunas personas usan para diferenciar las variables miembro de las variables locales.

En cuanto al error, parece que aMission tiene el tipo incorrecto. ¿Cuál es su declaración?


Es común en IDE's con intellisense; hará que sus variables miembro / módulo / clase se muestren en la parte superior de la lista. Otra versión previa común es "m_"
STW

1
si no significa nada, ¿cómo puede alternar entre _missionName y missionName como en mi ejemplo anterior? Mi declaración se ve así: Mission * aMission = [[Mission alloc] init]; aMission.missionName = @ "una misión";
Atma

1
Una es una variable de instancia y la otra es una propiedad. No puede acceder a variables de instancia con sintaxis como aMission.missionName, porque esa sintaxis no funciona con punteros.
Chuck

Además, tenga en cuenta que está intentando operar en un objeto Mission, pero la interfaz que ha publicado con la propiedad missionName es MissionCell.
smorgan 05 de

2

Esto es solo para la convención de nomenclatura de las propiedades de sintetización.

Cuando sintetice variables en el archivo .m, Xcode le proporcionará automáticamente inteligencia variable.


1

Tener un guión bajo no solo hace posible resolver sus ivars sin recurrir al uso de self.member sintaxis sino que hace que su código sea más legible ya que sabe cuándo una variable es un ivar (debido a su prefijo de guión bajo) o un argumento miembro (no guión bajo) )

Ejemplo:

- (void) displayImage: (UIImage *) image {

    if (image != nil) {
        // Display the passed image...
        [_imageView setImage: image];
    } else {
        // fall back on the default image...
        [_imageView setImage: _image];
    }
}

En este ejemplo, sería bueno ver una comparación del uso de self.image (o [self image]) también. ¿Cuándo es mejor usar self.image y cuándo es mejor usar _image?
Boeckm

2
@Boeckm: en general, debe usar self.image, que accede a la propiedad. La única vez que debe acceder a la variable de instancia _image, directamente está dentro de los initmétodos y el deallocmétodo, cuando llamar a cualquier otro método puede ser arriesgado (ya que el objeto está medio inicializado o medio desasignado).
Peter Hosey

1

Este parece ser el elemento "maestro" para preguntas sobre self.variableName vs. _variablename. Lo que me sorprendió fue que en el .h, tuve:

...
@interface myClass : parentClass {
className *variableName;    // Note lack of _
}

@property (strong, nonatomic) className  *variableName;
...

Esto lleva a self.variableName y _variableName siendo dos variables distintas en el .m. Lo que necesitaba era:

...
@interface myClass : parentClass {
className *_variableName;    // Note presence of _
}

@property (strong, nonatomic) className  *variableName;
...

Luego, en la clase '.m, self.variableName y _variableName son equivalentes.

Lo que aún no tengo claro es por qué muchos ejemplos todavía funcionan, incluso si esto no se hace.

Rayo


0

en lugar de guión bajo, puede usar el nombre self.variable o puede sintetizar la variable para usar la variable o la salida sin guión bajo.


2
si solo necesita la variable en la misma clase, simplemente declararla en el archivo .m en sí mismo, entonces le permitirá llamar sin uno mismo ni el guión bajo
Ansal Antony

0

Falta de las otras respuestas es que el uso le _variableimpide escribir distraídamentevariable y acceder al ivar en lugar de a la propiedad (supuestamente prevista).

El compilador lo obligará a usar cualquiera self.variableo _variable. El uso de guiones bajos hace que sea imposible escribir variable, lo que reduce los errores del programador.

- (void)fooMethod {

    // ERROR - "Use of undeclared identifier 'foo', did you mean '_foo'?"
    foo = @1;

    // So instead you must specifically choose to use the property or the ivar:

    // Property
    self.foo = @1;

    // Ivar
    _foo = @1;

}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.