web-dev-qa-db-fra.com

Pourquoi renommer les propriétés synthétisées dans iOS avec des traits de soulignement importants?

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

Lors de la création d'un nouveau projet dans Xcode 4, le code standard ajoute un caractère de soulignement lorsqu'il synthétise les ivars du fichier d'implémentation en tant que:

@synthesize window = _window;

ou:

@synthesize managedObjectContext = __managedObjectContext;

Quelqu'un peut-il me dire ce qui est accompli ici? Je ne suis pas un nube complet, mais c’est un aspect de l’objectif-C que je ne comprends pas.

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

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

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

Merci

126
Alpinista

Il s'agit d'un artefact d'une version précédente du moteur d'exécution Objective-C.

À l'origine, @synthesize était utilisé pour créer des méthodes d’accesseurs, 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éfixent leurs variables d'instance pour les différencier de leurs propriétés (même si Apple ne veut pas que vous utilisiez des traits de soulignement, mais c'est une autre affaire). Vous synthétisez la propriété pour pointer sur l'instance variable, mais le point est, _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; En utilisant plutôt la méthode d'accès, cela ressemblerait à ceci (bien que je ne le recommande pas, pour des raisons que j'expliquerai bientôt):

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

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

  • Vous pourriez finir par envoyer des notifications inattendues. D'autres objets peuvent observer les modifications apportées à qux, qui sont enregistrées lorsqu'une méthode d'accès est utilisée pour la modifier.
  • (Tout le monde n'est pas d'accord sur ce point :) Remettre à zéro le pointeur comme le fait l'accesseur peut masquer des erreurs de logique dans votre programme. Si vous accédez jamais à une variable d'instance d'un objet après l'objet a été désalloué, vous faites quelque chose de grave. En raison de la sémantique de messagerie de nil d'Objective-C, vous ne le saurez jamais, vous avez utilisé l'accesseur pour définir sur nil. Si vous aviez libéré la variable d'instance directement et que la référence n'avait pas été réinitialisée, l'accès à l'objet désalloué aurait provoqué un fort EXC_BAD_ACCESS.

Des versions ultérieures du moteur d'exécution 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

Ceci synthétise en fait une variable d’instance sur Foo appelée _qux, auquel on accède par les messages getter et setter -qux et -setQux:.

Je recommande contre cela: c'est un peu brouillon, mais il y a une bonne raison d'utiliser le trait de soulignement; à savoir, pour protéger contre un accès direct accidentel ivar. Si vous pensez pouvoir vous souvenir que vous utilisez une variable d'instance brute ou une méthode d'accès, procédez comme suit:

@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, dites simplement qux (ce qui se traduit par self->qux en syntaxe C pour accéder à un membre à partir d’un pointeur). Lorsque vous souhaitez utiliser des méthodes d'accesseurs (qui avertissent les observateurs, effectuent d'autres tâches intéressantes et rendent les opérations plus sûres et plus simples 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, c’est que le code exemple et le code modèle d’Apple sont nuls. Ne l'utilisez jamais comme guide pour le style correct Objective-C et certainement jamais comme guide pour une architecture logicielle appropriée. :)

223
Jonathan Sterling

Voici une autre raison. Sans souligner les variables d'instance, vous obtenez souvent un avertissement 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
13
Freeman

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

Non, ils ne sont pas. Ce sont des références aux propriétéswindow et viewController. C'est le point du trait de soulignement, pour que ce soit plus clair lorsque la propriété est utilisée (pas de soulignement) et quand l'accès direct à ivar (avec soulignement).

6
LaC

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

2
Krishna Kishore