web-dev-qa-db-fra.com

Chaque ivar doit-il être une propriété?

Je vois qu'il est recommandé partout lors du codage pour iOS que les propriétés soient utilisées pour accéder aux variables d'instance en raison des avantages que cela apporte à la gestion de la mémoire, entre autres.

Ce conseil ne me convient pas très bien. Je trouve que l'utilisation de propriétés au lieu de vieux ivars simples prend trop de code et je ne vois pas vraiment les avantages si vous êtes à l'aise avec la gestion de la mémoire. Est-ce vraiment si important? Quelle est votre approche de la gestion des variables d'instance?

56
Diego

Il n'est pas vraiment nécessaire de déclarer les propriétés de tous les ivars. Quelques points me viennent à l'esprit:

  • Si un ivar ne doit être affecté qu'à une seule fois pendant la durée de vie de l'objet, vous ne gagnez vraiment rien en déclarant une propriété. Il suffit de conserver/copier/attribuer pendant init puis de relâcher si nécessaire pendant dealloc.
  • Si un ivar doit être changé fréquemment, déclarer une propriété et toujours utiliser les accesseurs permettra d'éviter plus facilement les erreurs de gestion de la mémoire.
  • Vous pouvez déclarer des propriétés dans une extension de classe dans le fichier .m plutôt que dans le fichier .h si les propriétés et les ivars sont censés être privés.
  • Lorsque vous ciblez iOS 4.0+, vous n'avez pas du tout besoin de déclarer des ivars dans votre en-tête si vous définissez une propriété et synthétisez des accesseurs.

Donc j'utilise généralement des propriétés, mais pour des choses comme un NSMutableArray qu'un objet alloue pendant init et utilise pour contenir un tas de whatevers, j'utiliserai un vieil ivar ordinaire car je ne le ferai jamais être réaffecter l'ivar.

77
Daniel Dickison

Bien que la réponse de Daniel soit correcte, je pense qu'elle manque un point important. À savoir:

Je trouve que l'utilisation de propriétés au lieu de vieux ivars simples prend trop de code et je ne vois pas vraiment les avantages si vous êtes à l'aise avec la gestion de la mémoire.

Les avantages sont la cohérence; gestion cohérente de la mémoire et comportement cohérent.

Notamment, ces deux lignes de code peuvent avoir un comportement extrêmement différent lors de l'exécution:

iVar = [foo retain];
self.iVar = foo;

Le premier est un réglage direct de la variable d'instance et il n'y aura aucune notification de changement. La seconde passe par le setter et, ainsi, conserve toutes les personnalisations de sous-classe lors du set et garantit que tous les observateurs de la propriété sont informés du changement.

Si vous utilisez des ivars directement dans votre code (en interne à la classe - si vous utilisez des ivars d'une instance directement de à l'extérieur de cette instance, eh bien. .. tout entrepreneur travaillant sur votre base de code doit doubler ses tarifs;), vous devez également gérer la propagation des notifications de changement manuellement (généralement en appelant willChangeValueForKey:/didChangeValueForKey) ou conçoivent explicitement votre application pour éviter l'utilisation de mécanismes qui reposent sur l'observation de la valeur-clé.

Vous dites "prend trop de code". Je ne vois pas ça; dans les deux lignes de code ci-dessus, la syntaxe des points est moins de caractères. Même appeler la méthode setter en utilisant la syntaxe traditionnelle serait moins de code.

Et ne négligez pas la valeur de la gestion centralisée de la mémoire; une omission accidentelle dans une myriade de sites d'appel et de crash city.

51
bbum

Les propriétés ne sont que du sucre de syntaxe qui vous évitent d'écrire encore et encore les mêmes méthodes. Avec une propriété, vous disposez d'un setter qui libère l'ancien objet et conserve le nouveau gratuitement.

3
Simone D'Amico

Pour les champs privés - je suggère qu'il est sûr d'utiliser des ivars directs uniquement pour les types primitifs (BOOL/int/float, etc.). Je trouve une bonne pratique d'encapsulation tout liée à la gestion de la mémoire dans les propriétés - même dans les champs rarement utilisés. Le bonus supplémentaire de cette approche est que IDE met généralement en évidence l'accès direct aux ivars différemment, donc vous avez toujours une belle séparation des champs scalaires simples et des champs de type objet.

Contrairement à cela, je voudrais décourager fortement tout ivars direct dans l'interface publique de classe. En raison de la nature dynamique du langage, il peut entraîner des erreurs d'exécution extrêmement difficiles à trouver, à localiser et à corriger. Considérez la hiérarchie suivante

@interface BaseControl
...
@end

@interface Label : BaseControl
...
@end

@interface Button : BaseControl {
  @public
    BOOL enabled;
}
@end

et un extrait de code

- (void)enableAllButtons {
    NSArray *buttons = [self getAllButtons];   // expected to contain only Button instances
    for (Button *button in buttons) {
        button->enabled = YES;
    }
} 

Imaginez maintenant qu'il y a une erreur quelque part dans la logique -getAllButtons et que vous obtenez également des étiquettes retournées dans ce tableau - de sorte que ces instances de classe Label se verront attribuer ivar manquant. Le fait qui peut être surprenant est que -enableAllButtons ne se bloquera pas dans ce cas. Mais à ce stade, la structure interne de ces instances Label est corrompue et cela va provoquer un comportement indéfini et se bloquer quand elles sont utilisées ailleurs.

Comme certains problèmes courants avec la gestion de la mémoire (et en général - avec des pointeurs pendants) - ce type de problèmes est difficile à trouver et à localiser - car l'apparence de l'erreur est généralement éloignée (en termes de temps, de code ou de flux d'application) de la place, provoquant l'erreur. Mais avec ce problème particulier, vous n'avez même pas d'outils pratiques (comme des analyseurs de fuites/zombies, etc.) pour vous aider à le localiser et à le corriger - même lorsque vous apprenez à le reproduire et que vous pouvez facilement enquêter sur un état erroné.

De toute évidence, si vous utilisez @property (assign) BOOL enabled;, vous obtiendrez une exception d'exécution facile à diagnostiquer et à corriger dans -enableAllButtons.

1
Max O