126 votes

Pourquoi renommer les propriétés synthétisées dans iOS avec des caractères de soulignement en tête ?

Duplicata possible :
Comment fonctionne un trait de soulignement devant une variable dans une classe objective-c de cacao ?

Lors de la création d'un nouveau projet dans Xcode 4, le code boilerplate ajoute un caractère underscore lorsqu'il synthétise les ivars dans le fichier d'implémentation comme :

@synthesize window = _window;

ou :

@synthesize managedObjectContext = __managedObjectContext;

Quelqu'un peut-il me dire ce qui est accompli ici ? Je ne suis pas un nabot complet, mais c'est un aspect d'Objective-C que je ne comprends pas.

Un autre point de confusion ; dans l'implémentation du délégué de l'application, après avoir synthétisé l'iVar de la fenêtre comme ci-dessus, dans la méthode didFinishLaunchingWithOptions : de l'application, les ivars de la fenêtre et du viewController sont référencés en utilisant self :

self.window.rootViewController = self.viewController
[self.window makeKeyAndVisible];

mais dans la méthode dealloc c'est _window, ou _viewController

Merci

223voto

Jonathan Sterling Points 11628

Il s'agit d'un artefact d'une version antérieure du runtime Objective-C.

A l'origine, @synthesize a été utilisé pour créer des méthodes d'accès, mais le runtime exigeait toujours que les variables d'instance soient instanciées explicitement :

@interface Foo : Bar {
  Baz *_qux;
}

@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

- (void)dealloc {
  [_qux release];
  [super dealloc];
}

@end

Les gens préfixeraient leurs variables d'instance pour les différencier de leurs propriétés (même si Apple ne veut pas que vous utilisiez des caractères de soulignement, mais c'est une autre question). Vous synthétisez la propriété pour qu'elle pointe vers la variable d'instance. Mais le fait est que, _qux est une variable d'instance et self.qux (ou [self qux] ) est le message qux envoyé à l'objet self .

Nous utilisons la variable d'instance directement dans -dealloc ; l'utilisation de la méthode de l'accesseur à la place ressemblerait à ceci (bien que je ne le recommande pas, pour des raisons que je vais expliquer sous peu) :

- (void)dealloc {
  self.qux = nil; // [self setQux:nil];
  [super dealloc];
}

Cela a pour effet de libérer qux ainsi que la mise à zéro de la référence. Mais cela peut avoir des effets secondaires fâcheux :

  • Vous risquez de déclencher des notifications inattendues. D'autres objets peuvent observer des changements dans qux qui sont enregistrés lorsqu'une méthode d'accesseur est utilisée pour le modifier.
  • (Tout le monde n'est pas d'accord sur ce point :) Remettre à zéro le pointeur comme le fait l'accesseur peut cacher des erreurs de logique dans votre programme. Si vous accédez à une variable d'instance d'un objet après l'objet a été désalloué, vous faites quelque chose de très mal. En raison de l'approche de l'Objective-C nil -Cependant, vous ne le saurez jamais, puisque vous avez utilisé l'accesseur pour définir la valeur de l'option nil . Si vous aviez libéré la variable d'instance directement et n'aviez pas remis à zéro la référence, l'accès à l'objet désalloué aurait provoqué une forte alerte. EXC_BAD_ACCESS .

Les versions ultérieures du runtime ont ajouté la possibilité de synthétiser des variables d'instance en plus des méthodes d'accès. Avec ces versions du runtime, le code ci-dessus peut être écrit en omettant les variables d'instance :

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux = _qux;

- (void)dealloc {
  [_qux release];
  [super dealloc];
}

@end

Cela synthétise en fait une variable d'instance sur Foo appelé _qux qui est accessible par des messages getter et setter. -qux et -setQux: .

Je ne le recommande pas : c'est un peu désordonné, mais il y a une bonne raison d'utiliser le trait de soulignement ; à savoir, pour se protéger contre un accès direct accidentel à ivar. Si vous pensez que vous pouvez vous faire confiance pour vous rappeler si vous utilisez une variable d'instance brute ou une méthode d'accès, faites-le comme ceci à la place :

@interface Foo : Bar
@property (retain) Baz *qux;
@end

@implementation Foo
@synthesize qux;

- (void)dealloc {
  [qux release];
  [super dealloc];
}

@end

Ensuite, lorsque vous voulez accéder directement à la variable d'instance, il suffit de dire qux (ce qui se traduit par self->qux en syntaxe C pour accéder à un membre à partir d'un pointeur). Lorsque vous souhaitez utiliser les méthodes des accesseurs (qui notifieront les observateurs, et feront d'autres choses intéressantes, et rendront les choses plus sûres et plus faciles en ce qui concerne la gestion de la mémoire), utilisez self.qux ( [self qux] ) et self.qux = blah; ( [self setQux:blah] ).

Ce qui est triste ici, c'est que les exemples de code et les modèles de code d'Apple sont nuls. Ne vous en servez jamais comme guide pour un style Objective-C correct, et certainement pas comme guide pour une architecture logicielle correcte :)

13voto

Freeman Points 1725

Voici une autre raison. Sans souligner les variables d'instance, vous obtenez fréquemment des avertissements avec les paramètres self.title = title et self.rating = rating :

@implementation ScaryBugData
@synthesize title;
@synthesize rating;
- (id)initWithTitle:(NSString *)title rating:(float)rating {
    if (self = [super init]) {
        self.title = title; // Warning. Local declaration hides instance variable
        self.rating = rating; // Warning. Local declaration hides instance variable
    }
    return self;
}
@end

Vous évitez les avertissements en soulignant les variables d'instance :

@implementation ScaryBugData
    @synthesize title = _title;
    @synthesize rating = _rating;
    - (id)initWithTitle:(NSString *)title rating:(float)rating {
        if (self = [super init]) {
            self.title = title; // No warning
            self.rating = rating; // No warning
        }
        return self;
    }
    @end

6voto

LaC Points 7191

dans la méthode didFinishLaunchingWithOptions : de l'application, les ivars window et viewController sont référencés en utilisant self

Non, ils ne le sont pas. Ce sont des références à la propriétés window et viewController . C'est la raison d'être du trait de soulignement, qui permet d'indiquer plus clairement quand la propriété est utilisée (sans trait de soulignement) et quand on accède directement à l'ivar (avec trait de soulignement).

2voto

Oui, c'est juste pour différencier la référence de l'objet. C'est-à-dire que si l'objet est référencé directement, utilisez-le avec un trait de soulignement, sinon utilisez self pour référencer l'objet.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X