@property/@question de synthèse
-
29-10-2019 - |
Question
Je suis en train de parcourir toute ma documentation concernant la gestion de la mémoire et je suis un peu confus à propos de quelque chose.
Lorsque vous utilisez @property, cela crée des getters/setters pour l'objet :
.h :@property (conserver, non atomique) NSString *myString
.m :@synthesize maChaîne
Je comprends cela, mais là où je suis confus, c'est l'utilisation de soi.Je vois une syntaxe différente dans différents blogs et livres.J'ai vu:
myString = [NSString alloc] initWithString:@"Hi there"];
ou
self.myString = [NSString alloc] initWithString:@"Hi there"];
Puis dans dealloc je vois :
self.myString = nil;
ou
[myString release];
ou
self.myString = nil;
[myString release];
Sur ce site, quelqu'un a déclaré que l'utilisation de self ajoute un autre incrément au nombre de rétentions ?Est-ce vrai, je n'ai vu ça nulle part.
Les getters/setters automatiques fournis sont-ils automatiquement libérés ?
Quelle est la bonne façon de faire tout cela ?
Merci!
La solution
Si vous n'utilisez pas la syntaxe dot, vous n'utilisez aucun setter ou getter.
La chose suivante est que cela dépend de la manière dont la propriété a été déclarée.
Supposons quelque chose comme ceci:
@property (nonatomic, retain) Article *article;
...
@synthesize article;
Attribuer quelque chose à l'article avec
self.article = [[Article alloc] init];
surchargera l'instance rendue par alloc / init et provoquera une fuite. En effet, l'auteur de l'article le conservera et publiera toute instance précédente pour vous.
Vous pouvez donc le réécrire comme suit:
self.article = [[[Article alloc] init] autorelease];
Faire ceci
article = [[Article alloc] init];
est également correct, mais pourrait impliquer une fuite car l'article peut déjà contenir une référence à une instance. Il serait donc nécessaire de libérer la valeur au préalable:
[article release];
article = [[Article alloc] init];
Libérer de la mémoire peut être fait avec
[article release];
ou avec
self.article = nil;
Le premier accède directement au champ, aucun setters / getters n'est impliqué. Le second met nul au champ en utilisant un setter. Ce qui libérera l'instance actuelle, s'il y en a une avant de la définir sur nil.
Cette construction
self.myString = nil;
[myString release];
est tout simplement trop, cela envoie en fait une libération à zéro, ce qui est inoffensif mais également inutile.
Il vous suffit de mapper mentalement le chapeau en utilisant la syntaxe du point en utilisant des méthodes d'accesseur:
self.article = newArticle
// is
[self setArticle:newArticle];
et
myArticle = self.article;
// is
myArticle = [self article];
Quelques suggestions de lecture, tous les documents officiels d'Apple:
Le langage de programmation Objective-C
Guide de programmation de la gestion de la mémoire
Autres conseils
Lorsque vous créez un retain
setter, vous créez quelque chose comme ceci :
- (void)setString:(NSString *)someString {
if (someString != string) {
[string release];
[someString retain];
string = someString;
}
}
Si vous n'utilisez pas le setter, la nouvelle valeur n'obtient pas cette retenue : vous ne "possédez" pas cette chaîne, et comme ce sont toutes des références, si la chaîne d'origine est libérée, vous pourriez être confronté à une référence nulle, ce qui conduira à un EXC_BAD_ACCESS
.L'utilisation du setter garantit que votre classe dispose désormais d'une copie de cette valeur. Donc oui, cela incrémente le nombre de conservations de la nouvelle valeur.(Notez que l'utilisation du getter est une convention de la POO : les étrangers ne devraient pas pouvoir toucher directement l'ivar.Également dans votre getter, vous pouvez modifier la valeur, en renvoyant peut-être un NSArray lorsque votre ivar est un NSMutableArray, par exemple).
Tu ne devrais pas autorelease
dans un setter - Apple l'a utilisé dans son exemple de code, mais il faut garder à l'esprit que les setters sont appelés beaucoup - des millions de fois, potentiellement.Tous ces objets vont dans le même pool de libération automatique, donc à moins que vous ne créiez le vôtre et/ou que vous le vidiez régulièrement, vous aurez une tonne d'éléments dans votre pool, tous inutiles mais occupant toujours de la RAM.Il vaut mieux simplement release
.
Quant au dealloc, remontez jusqu'à ce setter.Si vous envoyez un release
directement, c'est évident : vous libérez cet objet.Mais si tu écris self.string = nil;
, ce que tu fais c'est ça :
- La valeur nulle n'est pas la même, vous entrez donc la
if
bloc - Vous libérez l'ancienne valeur : ce que vous voulez faire
- Toi
retain
néant:les messages à zéro ne font rien et vous ne crashez pas - Vous définissez nil, qui ne prend aucune mémoire, sur la chaîne, qui est désormais effectivement vide
Par convention, j'utilise release
dans mon dealloc
méthode, parce que release
semble plus définitif, et dealloc
est le dernier appel de méthode que votre objet recevra.j'utilise self.string = nil;
dans viewDidUnload et les méthodes d'avertissement de mémoire.
J'espère que cela t'aides!
En plus de la réponse de Nick - les getters / setters synthétisés ne fournissent pas d'autorelease (btw, quelle est la grande idée de faire cela? Eh bien, vous pouvez utiliser getter comme une usine, mais ce n'est pas une méthode courante en Objective C).
Ensuite, dans dealloc, je vois:
self.myString= nil;
ou
[version myString];
ou
self.myString= nil;[myString libération];
Dans dealloc, peu importe la forme de version que vous utilisez.Mais le bon moyen est de supprimer vos champs lors de leur publication :) Je préfère utiliser self.myString = nil;
dans dealloc