Erreur dans la doc Apple ?: satanées properties décidément !

ClicCoolClicCool Membre
novembre 2009 modifié dans API AppKit #1
Sans doutes me trompe-je mais je ne parvient pas à  comprendre pourquoi, dans cet exemple de la doc, Apple fait un release sur une propriété implicitement assignée.

L'exemple se trouve dans The Objective-C Programming Language: Declared Properties: Example
The Link protocol declares a property, next.
MyClass adopts the Link protocol so implicitly also declares the property next. MyClass also declares several other properties.../...


Le code cité en exemple:
@protocol Link<br />@property id &lt;Link&gt; next;<br />@end<br /> <br /> <br />@interface MyClass : NSObject &lt;Link&gt;<br />{<br />    NSTimeInterval intervalSinceReferenceDate;<br />    CGFloat gratuitousFloat;<br />    id &lt;Link&gt; nextLink;<br />}<br />@property(readonly) NSTimeInterval creationTimestamp;<br />@property(copy) NSString *name;<br />@property CGFloat gratuitousFloat;<br />@property(readonly, getter=nameAndAgeAsString) NSString *nameAndAge;<br /> <br />@end<br /> <br /> <br />@implementation MyClass<br /> <br />@synthesize creationTimestamp = intervalSinceReferenceDate, name;<br />// Synthesizing &#39;name&#39; is an error in legacy runtimes;<br />// in modern runtimes, the instance variable is synthesized.<br /> <br />@synthesize next = nextLink;<br />// Uses instance variable &quot;nextLink&quot; for storage.<br /> <br />@dynamic gratuitousFloat;<br />// This directive is not strictly necessary.<br /> <br />- (CGFloat)gratuitousFloat {<br />    return gratuitousFloat;<br />}<br />- (void)setGratuitousFloat:(CGFloat)aValue {<br />    gratuitousFloat = aValue;<br />}<br /> <br /> <br />- (NSString *)nameAndAgeAsString {<br />    return [NSString stringWithFormat:@&quot;%@ (%fs)&quot;, [self name],<br />             &nbsp; [NSDate timeIntervalSinceReferenceDate] - intervalSinceReferenceDate];<br />}<br /> <br /> <br /> - (id)init {<br />    if (self = [super init]) {<br />        intervalSinceReferenceDate = [NSDate timeIntervalSinceReferenceDate];<br />    }<br />    return self;<br />}<br /> <br />- (void)dealloc {<br />    [nextLink release];<br />    [name release];<br />    [super dealloc];<br />}<br /> <br />@end


On y voit clairement que la property link n'a pas d'attribut et est donc Assign par défaut.
@property id &lt;Link&gt; next;


La classe adoptant le protocole implémente une variable d'instance pour supporter cette propriété.
id &lt;Link&gt; nextLink;


Puis s'appuis sur @synthétize pour générer les accesseurs en précisant l'iVar à  utiliser.
@synthesize next = nextLink;

Les accesseurs ne sont pas sensés faire un retain sur une propriété Assign fusse seulement par défaut non ?

Mais alors pourquoi dans le dealloc Apple release-t-il cette iVar ? ???
[nextLink release];

Réponses

  • AliGatorAliGator Membre, Modérateur
    21:37 modifié #2
    En effet, étrange...
    Tu as essayé le code sur un projet d'essai pour voir si ça crash ?
    Ca peut valoir le coup d'utiliser les liens en bas de page pour signaler l'erreur si c'est bien le cas.
  • ClicCoolClicCool Membre
    21:37 modifié #3
    Merci de ta réponse rapide, 'me sens moins seul maintenant !

    J'vais essayer d'implémenter un truc comme ça dès que j'ai une minute pour voir, avant d'éventuellement signaler ça à  Apple.
  • dilarogadilaroga Membre
    21:37 modifié #4
    Ca ne serait pas une manière déguisée et fourbe pour mettre la propriété next à  nil. Genre un racourci pour [self setNext:nil]...
  • AliGatorAliGator Membre, Modérateur
    21:37 modifié #5
    [tt]self.next = nil[/tt] (ou [tt][self setNext:nil][/tt]) va appeler le setter de la propriété next pour la remettre à  nil... ce qui, si le setter est en fait une @property(retain), va effectivement faire un release sur la valeur qu'il y avait dans next (...puis un retain ensuite sur... nil, donc inopérant), et si la @property est en assign, ne va pas faire de release (mais juste l'affectation).

    Donc justement c'est plutôt dans le cas inverse que ça aurait marché dilaroga : self.next = nil fait un release ou non suivant si la @property associée est (retain) ou (assign).
    Mais là  c'est un message "release" qui est explicitement envoyé (qui décrémente le retainCount de la variable, mais ne la met pas à  nil pour autant, ce qui fait à  la limite qu'elle peut pointer sur un emplacement mémoire qui ne va plus rien représenter si le retainCount est passé à  zéro suite à  ce release et que l'objet a été désalloué, mais on s'en fiche on est dans le dealloc la variable ne peut plus être utilisée après ça de toute façon).
    Alors que la @property ne fait pas de retain, et qu'il n'y a ni retain ni copy dans le reste du code sur cette variable next à  priori... donc pourquoi le release... mystère.
  • dilarogadilaroga Membre
    21:37 modifié #6
    (...)Donc justement c'est plutôt dans le cas inverse que ça aurait marché dilaroga : self.next = nil fait un release ou non suivant si la @property associée est (retain) ou (assign).
    Mais là  c'est un message "release" qui est explicitement envoyé(...)

    D'accord mais l'ivar et la propriété sont intimement liées (à  la compilation ou au runtime ?) par la déclaration
    @synthesize next = nextLink
    

    Si dans le code tu affectes directement une valeur à  l'ivar nextLink :
    nextLink = aLink
    

    c'est bien cette même valeur que tu récupéreras plus tard en passant par le getter
    aLink = self.next
    

    La correspondance semble fonctionner dans les deux sens ou bien je me trompe ?
    Alors le release reçu par nextLink décrémente son retainCount et si le retainCount est à  zéro la méthode -dealloc de cet objet est appelée afin de le détruire de la mémoire immédiatement (de sorte qu'un appel utltérieur à  cet objet provoquera le plantage du programme). Donc, le release décrémente et désalloue au besoin. Donc ici si le retainCount est à  zéro mais j'en doute une assignation devrait tout de même le faire passer à  1 (sinon comment s'en sortirait le pool de ramassage pour savoir ce qui est à  détruire ou non ?) la référence sera bel et bien supprimée et tout prochain appel à  nextLink povoquera une erreur tout comme un appel à  next (puisque l'ivar et la propriété sont unies).


  • ClicCoolClicCool Membre
    21:37 modifié #7
    Houla, il me semble que tu mélanges un peu les choses.... ???
  • AliGatorAliGator Membre, Modérateur
    21:37 modifié #8
    Il faut peut-être que tu ailles relire mon tutoriel sur les @property, dilaroga :P
    Envoyer un release sur une variable ne la passe pas à  nil, même si cette variable est liée à  une @property. Une @property ne fait que déclarer les setters et getters d'une variable.

    Appeller [toto release] va envoyer un message release à  toto comme [toto tata] va envoyer le message tata à  toto. Ca ne va en aucun cas changer l'affectation de la variable à  qui on envoie le message, en l'occurrence toto. Ca peut affecter l'état de l'objet référencé par la variable (l'objet pointé par la variable toto), mais la variable, elle, pointera toujours sur le même emplacement mémoire et ne sera pas affectée à  un autre objet si on lui envoie juste un message.
  • dilarogadilaroga Membre
    21:37 modifié #9
    Il faut peut-être que tu ailles relire mon tutoriel sur les @property

    Le lire déjà  serait pas mal mais je ne l'ai pas trouvé par ici... ::)
    Une @property ne fait que déclarer les setters et getters d'une variable.

    Merci, c'est plus clair ainsi pour moi c'est l'affectation qui associe la propriété à  une ivar d'un autre nom qui m'a perturbé.
  • AliGatorAliGator Membre, Modérateur
    21:37 modifié #10
    En effet il n'est pas forcément mis en avant.
    Voilà  où j'avais écrit ma prose

    J'admets que ça peut être assez indigeste (j'avais pondu ça d'une traite sans trop formater ça proprement sous forme d'article ou de tuto propre) car beaucoup de texte, mais je suis parti d'un exemple qui met en avant à  peu près tous les cas... j'espère que ça va t'aider à  y voir plus clair ;D
Connectez-vous ou Inscrivez-vous pour répondre.