Comment fonctionne un trait de soulignement devant une variable dans une classe objectif-c cacao?

157

J'ai vu dans quelques exemples iPhone que les attributs ont utilisé un trait de soulignement _ devant la variable. Quelqu'un sait-il ce que cela signifie? Ou comment ça marche?

Un fichier d'interface que j'utilise ressemble à:

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

@property (nonatomic, retain) UILabel *missionName;

- (Mission *)mission;

Je ne sais pas exactement ce que fait ce qui précède, mais lorsque j'essaie de définir le nom de la mission comme:

aMission.missionName = missionName;

J'obtiens l'erreur:

demande de membre 'missionName' dans quelque chose qui n'est pas une structure ou un syndicat

Atma
la source

Réponses:

97

Si vous utilisez le préfixe de soulignement pour vos ivars (qui n'est rien de plus qu'une convention commune, mais utile), alors vous devez faire 1 chose supplémentaire pour que l'accesseur généré automatiquement (pour la propriété) sache quel ivar utiliser. Plus précisément, dans votre fichier d'implémentation, vous synthesizedevriez ressembler à ceci:

@synthesize missionName = _missionName;

Plus génériquement, c'est:

@synthesize propertyName = _ivarName;
Kelan
la source
78
avec des propriétés d'auto-synthèse, ce n'est plus nécessaire. Xcode synthétise un @property xxxx avec un ivar nommé _xxxx dans les coulisses. Soigné.
LearnCocos2D
@ LearnCocos2D Salut! Un débutant sur iOS ici et il y a quelque chose que je dois clarifier. Pour tout ce temps ce que je faisais était déclarer propertydans le fichier .h et dans l'accès .m je l'aide FIE selfcomme si, self.someProperty. Est-ce la bonne manière? Ou devrais-je utiliser les ivars dans le code?
Isuru
définir l'ivar ne lance pas le setter de propriétés - vous décidez si c'est une bonne idée ou non pour chaque cas particulier
LearnCocos2D
Question Noob: pourquoi ne pas utiliser directement les ivars? pourquoi devrais-je déclarer une var distincte pour contenir l'ivar?
Allen
1
@Allen, si je comprends bien votre question: la variable distincte que vous déclarez est un pointeur vers la variable réelle. Ceci est important pour quelques raisons (que je connais) Premièrement, lorsque vous passez un pointeur dans une fonction, vous ne dupliquez pas sa valeur. Vous dites simplement à la fonction où trouver la valeur à utiliser. Cela aide à garder votre mémoire utilisée faible (et aide également à allouer et à désallouer de la mémoire, ce qui est important en l'absence de `` garbage collection '' que vous trouverez en Java)
David Sigley
18

C'est juste une convention pour la lisibilité, cela ne fait rien de spécial au compilateur. Vous verrez des gens l'utiliser sur des variables d'instance privées et des noms de méthodes. Apple recommande en fait de ne pas utiliser le trait de soulignement (si vous ne faites pas attention, vous pouvez remplacer quelque chose dans votre superclasse), mais vous ne devriez pas vous sentir mal à l'idée d'ignorer ce conseil. :)

Marc Charbonneau
la source
19
D'après ce que je comprends, Apple recommande de ne pas utiliser le préfixe de soulignement sur les noms de méthodes (ils le réservent pour eux-mêmes comme convention pour les méthodes privées), mais ils n'ont aucune recommandation de ce type sur les noms de variables d'instance.
Kelan
9
@Kelan En fait, Apple encourage à le faire : "Habituellement, vous ne devriez pas accéder directement aux variables d'instance, à la place vous devriez utiliser des méthodes d'accès (vous accédez directement aux variables d'instance dans les méthodes init et dealloc). Pour aider à signaler cela, préfixez instance noms de variables avec un trait de soulignement (_), par exemple: \ @implementation MyClass {BOOL _showsTitle;} "
dmirkitanov
En fait, je ne pense pas qu'Apple nous encourage à le faire, car tous leurs propres exemples de codes dans la bibliothèque des développeurs iOS ne contiennent pas le ( ). Apple dit également qu'ils l'ont réservé, ce qui doit signifier qu'ils l'utilisent en interne pour leurs propres frameworks comme UIKit, etc. C'est pourquoi nous ne devons pas l'utiliser négligemment. Mais je vois cela, dans le lien que vous avez fourni @kelan. Ils disent en fait dans "l'historique des révisions" qu'il est "approprié" d'utiliser ( ). J'interprète que nous «pouvons» l'utiliser si nous le voulons.
WYS
La documentation d'Apple qui dit de ne pas utiliser le préfixe de soulignement pour les noms de méthode est ici .
ThomasW
9

Le seul objectif utile que j'ai vu est de faire la différence entre les variables locales et les variables membres comme indiqué ci-dessus, mais ce n'est pas une convention nécessaire. Lorsqu'il est associé à un @property, il augmente la verbosité des instructions de synthèse - @synthesize missionName = _missionName;et est moche partout.

Au lieu d'utiliser le trait de soulignement, utilisez simplement des noms de variables descriptifs dans des méthodes qui ne sont pas en conflit. Lorsqu'elles doivent entrer en conflit, le nom de la variable dans la méthode doit subir un trait de soulignement, pas la variable membre qui peut être utilisée par plusieurs méthodes . Le seul endroit commun où cela est utile est dans un setter ou dans une méthode init. De plus, cela rendra l'instruction @synthesize plus concise.

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

Edit: Avec la dernière fonctionnalité du compilateur d'auto-synthèse, j'utilise maintenant le soulignement pour l'ivar (dans les rares occasions où j'ai besoin d'utiliser un ivar pour correspondre à ce que fait l'auto-synthèse.

Peter DeWeese
la source
C'est l'inverse. la variable privée est soulignée. la propriété non. et en les synthétisant, vous les associez.
Justin le
C'est exactement ce que je décris, sauf que je l'ai appelé une "variable membre" au lieu d'une "variable privée".
Peter DeWeese du
Aie! Cela pose des problèmes… l'auto-synthèse fera l'ivar _myString ce qui signifie que votre setter ne fonctionnera pas (car il ne pourra alors pas distinguer votre ivar à partir du paramètre de méthode).
geowar
Correct, c'est pourquoi j'ai ajouté la modification à la fin lorsque Apple a ajouté l'auto-synthèse.
Peter DeWeese
5

Cela ne veut vraiment rien dire, c'est juste une convention que certaines personnes utilisent pour différencier les variables membres des variables locales.

En ce qui concerne l'erreur, il semble qu'uneMission ait le mauvais type. Qu'est-ce que c'est sa déclaration?

smorgan
la source
C'est courant dans les IDE avec intellisense; cela fera apparaître vos variables membre / module / classe en haut de la liste. "M_"
STW
1
si cela ne veut rien dire, comment pouvez-vous basculer entre _missionName et missionName comme dans mon exemple ci-dessus? Ma déclaration ressemble à: Mission * aMission = [[Mission alloc] init]; aMission.missionName = @ "une mission";
Atma
1
L'une est une variable d'instance et l'autre est une propriété. Vous ne pouvez pas accéder aux variables d'instance avec une syntaxe telle que aMission.missionName, car cette syntaxe ne fonctionne pas avec des pointeurs.
Chuck
Notez également que vous essayez d'opérer sur un objet Mission, mais que l'interface que vous avez publiée avec la propriété missionName est une MissionCell.
smorgan
2

C'est uniquement pour la convention de dénomination des propriétés de synthèse.

Lorsque vous synthétisez des variables dans le fichier .m, Xcode vous fournira automatiquement _variable intelligence.

Dipak Narigara
la source
1

Avoir un trait de soulignement permet non seulement de résoudre vos ivars sans recourir à la syntaxe self.member, mais cela rend votre code plus lisible puisque vous savez quand une variable est un ivar (à cause de son préfixe de soulignement) ou un argument membre (pas de trait de soulignement ).

Exemple:

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

    if (image != nil) {
        // Display the passed image...
        [_imageView setImage: image];
    } else {
        // fall back on the default image...
        [_imageView setImage: _image];
    }
}
Jason Fuerstenberg
la source
Dans cet exemple, il serait bien de voir une comparaison de l'utilisation de self.image (ou [self image]) également. Quand est-il préférable d'utiliser self.image et quand est-il préférable d'utiliser _image?
Boeckm
2
@Boeckm: Généralement, vous devez utiliser self.image, qui accède à la propriété. Le seul moment où vous devez accéder à la variable d'instance,, _imageest directement dans les initméthodes et la deallocméthode, lorsque l'appel de toute autre méthode peut être risqué (puisque l'objet est à moitié initialisé ou à moitié désalloué).
Peter Hosey
1

Cela semble être l'élément «maître» pour les questions sur self.variableName par rapport à _variablename. Ce qui m'a jeté pour une boucle, c'est que dans le .h, j'avais:

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

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

Cela conduit à self.variableName et _variableName étant deux variables distinctes dans le .m. Ce dont j'avais besoin était:

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

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

Ensuite, dans la classe '.m, self.variableName et _variableName sont équivalents.

Ce que je ne suis toujours pas clair, c'est pourquoi de nombreux exemples fonctionnent encore, même si cela n'est pas fait.

Rayon

RayInNoIL
la source
0

au lieu du trait de soulignement, vous pouvez utiliser le nom self.variable ou vous pouvez synthétiser la variable pour utiliser la variable ou la sortie sans trait de soulignement.

SARATH SASI
la source
2
si vous n'avez besoin que de la variable dans la même classe, déclarez-la simplement dans le fichier .m lui-même, cela vous permettra d'appeler sans soi ni le soulignement
Ansal Antony
0

Il manque dans les autres réponses que l'utilisation _variablevous empêche de taper variableet d'accéder par inadvertance à l'ivar plutôt qu'à la propriété (présumée prévue).

Le compilateur vous obligera à utiliser soit self.variableou _variable. L'utilisation de traits de soulignement rend la saisie impossible variable, ce qui réduit les erreurs du programmeur.

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

}
pkamb
la source