[[object retain] autorelease] explication
bonsoir,
je suis un tuto et j'ai
je suis perplexe sur la derniere ligne.
1) vu son nom la methode doit renvoyer un object autorelease
2) creation en autorelease (ok)
..../...
3) pourquoi le retenir et l'autoreleaser de nouveau??
cette ligne est a supprimer ? ou ai je loupé un truc?
merci
je suis un tuto et j'ai
<br />- (NSDictionary *)propertyList<br />{<br /> NSMutableDictionary *dictionary = [NSMutableDictionary dictionaryWithCapacity:5];<br /><br /> ..../....<br /><br /> return [[dictionary retain] autorelease];<br />}<br /><br />
je suis perplexe sur la derniere ligne.
1) vu son nom la methode doit renvoyer un object autorelease
2) creation en autorelease (ok)
..../...
3) pourquoi le retenir et l'autoreleaser de nouveau??
cette ligne est a supprimer ? ou ai je loupé un truc?
merci
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Par contre j'en suis venu à me méfier énormément de mes dictionnaires créés par des méthodes de convenance comme celle utilisée ici. J'ai eu tellement souvent de EXC_BAD_ACCESS où Instruments trouvait un dictionnaire responsable que j'ai pris l'habitude de les déclarer avec alloc]init] et placer mon autorelease plus loin dans le code.
Le souci vient, m'a t'il semblé, de ce qu'ils sont placés dans l'autorelease pool de l'événement en cours (le process lancé par l'appui sur un bouton par exemple) qui est créé automatiquement par Cocoa au début de l'action et vidé à la fin. Donc un "release" lui est envoyé et le tableau a un objet qui sera absent au prochain appel.
C'est ce que j'avais cru en comprendre, je n'avais pas pensé à faire retain]autorelease] ça aurait peut être fonctionné.
que a la création il est placer dans le pool autorelease de "l'action" (que je ne connaissais pas), et que le retain]autorelease] le place dans le pool "générale"?
http://vgable.com/blog/2009/03/31/how-to-write-cocoa-object-getters/
Le vocabulaire que tu utilises est maladroit...
Le runtime ObjC possède une boucle d'événements. Au tout début de la boucle est créé un autorelease pool.
Ensuite, le runtime récupère les événements (clics, frappe clavier, etc.) auprès de l'OS et les convertit en NSEvents, puis les retransmet à qui de droit (vue cliquée, premier répondeur, etc.).
Ainsi, si on clique un bouton, le bouton envoie une message d'action à sa cible; c'est dans la méthode d'action que notre code entre en scène.
Enfin, quand tous les événements ont été traités, on arrive à la fin de la boucle d'événements où l'autorelease pool est vidé: tous les objets qui y ont été ajoutés par un message -[autorelease] recoivent un -[release].
Ceci signifie que les objets autoreleasés n'existent que pendant l'itération actuelle de la boucle, ce qui suffit à notre code pour créer des objets et les transmettre à d'autres méthodes. Cela signifie aussi que:
- si un objet doit être conservé plus d'une itération de la boucle, il faut le créer par +[alloc].
- si on reçoit un objet autoreleasé et qu'on souhaite le conserver au-delà d'une itération de la boucle, il faut lui envoyer un -[retain].
C'est intéressant, mais je ne suis pas convaincu par l'argumentation: si le getter me renvoie un objet, alors par convention (le nom de la méthode ne commence par alloc, new, copy ou create), je dois faire comme s'il était autoreleasé:
- l'objet qui détient l'objet renvoyé ne peut de toute façon pas lui envoyer un -[release] tant que ma méthode s'exécute.
- si elle veut conserver l'objet, la méthode actuelle doit de toute façon lui envoyer un -[retain]
ouai, c'est fumeux.
je préfère 10 fois le premier commentaire.
quand au "pool de l'action" existe t'il?, et les objets crées (autorelease) pendant le "déroulement de l'action" lui sont ils assignées?
je n'ai jamais lu un truc pareil....
Non, il y a un seul pool à chaque itération de la boucle de messages, sauf si, évidemment, le développeur en créée un explicitement.
D'une manière générale, les pools sont gérés sur une pile, de sorte qu'autorelease place l'objet concerné dans le pool situé au somment de la pile.
Pour moi, c'est clairement la faute du programmeur si ça plante.
De même pour :
Perso, je ne fais jamais de copy.
Par contre, je ne comprends pas ceci: (enfin si, c'est le même prob qu'au-dessus)
Et justement, pour les setters, j'utilise cette macro:
Google Objective-C Style Guide
inverser les deux lignes ne suffit pas?
[new retain];
[old release];
old = new;
Ex. 1: Comme tu le dis, c'est une faute grossière.
Ex. 2: Si la propriété est déclarée en NSString, d'un point de vue conceptuel, il est de la responsabilité de l'objet qui la fournit qu'elle ne soit pas modifiée (c'est quand même à ça que servent les objets-valeurs)
Ex. 3: "It's less prone to error". ça c'est de l'argumentation
L'argument [tt]// Won't dealloc if |foo_| == |aFoo|[/tt] ne tient pas: si l'objet qui nous le transmet nous fournit aFoo, c'est qu'il ne l'a pas encore désalloué.
Le code habituel est meilleur:
Si on s'est trompé dans les -[release] ça plantera dans cette méthode, pas plus tard quand l'autorelease pool sera vidé.
ne tient pas compte du fait que aFoo et foo peuvent etre le meme objet (ATTENTION)
mais ça:
Si l'objet est le même, alors le release count est de 2 quand on appelle setFoo: (l'objet appelant retient l'objet, et l'objet appelé retient l'objet).
En fait, ça dépend de comment sont écris les getters...
Dans l'exemple du setter de Ceroce, si le getter est:
alors "[obj setFoo:[obj foo]]" va surement faire planter le code de céroce (m'enfin bon a priori, écrire cela n'a absolument aucun intérêt). Maintenant, si le getter fait un "retain autorelease", le code du setter de céroce est correct.
EDIT: (Pardon, j'ai mal lu ta réponse zoc, effectivement, tu as généralisé).
C'est ça le problème, tant que l'on code pour soit uniquement, on sait ce que l'on fait, mais dès que l'on distribue le code, alors il faut se mettre en mode "paranoà¯aque", car on ne peut pas savoir comment les autres programment.
retain] autorelease] == exotique!
la bonne façon du setter en mode retain
[new retain];
[old release];
old = new;
a+
En effet, ça permet si jamais l'objet A, qui contient l'objet B qu'on récupère du getter, est releasé entre temps, de ne pas tout foutre en l'air.
Exemple : Si le getter de "firstName" par exemple est juste implémenté comme cela : Alors si on a un code comme le suivant, qui pourtant parait tout à fait correct et n'est pas spécialement une faute, ça va crasher : En effet, comme l'objet Person a reçu un release entre temps, il a été détruit, son dealloc a été appelé, la chaà®ne firstName a été released et détruite entre temps... et au final l'objet qui avait été retourné par [tt][p firstName][/tt], et qui était valide à ce moment là , ne l'est plus après le [tt][p release][/tt] ! Pourtant quand on lit le code qui utilise l'objet Person, ça ne saute pas aux yeux (et en plus comme on n'est pas sensé connaà®tre l'implémentation de la classe Person (abstraction boite noire) on n'a pas à aller regarder dans le code de Person.m pour réfléchir à si on doit faire un retain de fn ou pas...)
Alors que si le getter de "firstName" est écrit avec [tt]return [[_firstName retain] autorelease];[/tt] là il n'y a plus de risque. C'est d'ailleurs comme ça, si je ne dis pas de bétises, que les @property synthétisées automatiquement par @synthesize sont écrites.
En conclusion :
- Soit vous utilisez des @property et des @synthesize et les setters et getters vont être écrits automatiquement avec la bonne forme et vous ne risquez pas d'avoir d'ennuis
- Soit si vous écrivez vos setters et getters vous-même pensez :
- soit à envoyer un "autorelease" à l'ancienne valeur dans le setter, au lieu d'un simple release
- mais de toute façon à faire retain+autorelease dans le getter pour pas tomber sur le bug du container qui est releasé et release son contenu implicitement !
c'est plus clair
Oui, je sais, toutes mes excuses, je manque de l'usinage scolaire... La doc parle mieux que moi:
http://developer.apple.com/library/mac/documentation/DeveloperTools/Conceptual/IB_UserGuide/Layout/Layout.html#//apple_ref/doc/uid/TP40005344-CH19-SW23
Et c'est comme ça qu'une fenêtre qu'on bouge peut planter l'application: à la sortie, quand on l'affiche, l'objet est là , puis quand il faut redessiner il n'est plus là .. Peut être qu'un retain]autorelease] aurait arrangé les choses, j'essayerais la prochaine fois, si j'y pense...
D'un autre côté, justement, créer des objets avec alloc+init (et les relâcher soi même) est plus clair, plus "carré". Et souvent les objets Cocoa offrent plus de possibilité de création à partir d'autres objets dans les méthodes d'instance que via les méthodes de classe.
Finalement, pour débuter je recommanderais plutôt les alloc]initWith...:] release (+ loin)/autorelease], et là on ne sent pas le besoin de rajouter un retain ;-) Mais pour revenir à la question de départ je présume que l'auteur du livre aurait pu prendre ma signature ...
S'il faut redessiner, c'est que tu es dans une itération suivante de la boucle, alors ça plante si l'objet n'a pas été retenu.
ok, j'ai bien compris le coup du "container releasé" qui "release sa property" dans la meme boucle d'événement.
mais dans le cas du code :
dictionary n'est pas une property, et donc retain] autorelease] ne sert a rien?
Effectivement. la méthode "dictionnaryWithCapacity" de NSMutableDirectory retourne (toujours comme son nom l'indique) un objet déjà autoreleasé.
Exemple d'une classe C++ avec une variable d'instance de type CFStringRef:
Si je fais:
Ca ne plante pas, mais ça m'affiche:
Mais name est bien releasé !
Donc, il faut bien faire un retain sur name avant le setName()
En C++, on a l'habitude de faire des copies d'objets, à la condition d'implémenter le constructeur de copie
et l'opérateur d'affectation,
donc on peut faire:
MyClass c1(c);
et là plus de problème: CFShow(c1.name(); affiche bien "Alfred"
Et par ailleurs, on voit que le C++ c'est quand même bien relou !
Certes il y a des cas bien spécifiques où tu peux être amener à ne faire que du CoreFoundation sans Cocoa (ou avec mais sans toll-free-bridging ou autre mécanisme permettant d'accéder au mécanisme d'autorelease), mais dans ce cas ce ne sont de toute façon plus les mêmes règles qui sont utilisées pour la gestion mémoire (on tourne plutôt autour de la Create Rule & co, du coup les fonctions qui créent des objets ne sont plus forcément celles qui les détruisent puisqu'il n'y a justement pas de notion d'autorelease) donc c'est une autre façon de voir les choses.
Dans le cas le plus courant utilisé ici, où l'on utilise Cocoa et très peu CoreFoundation directement, encore moins mixé avec du C++, utiliser retain+autorelease pour les raisons évoquées plus haut me semble être approprié. Après je dis pas que dans les cas d'environnement moins courants tels que celui que tu présentes c'est pas gênant mais ça reste moins courant et de toute façon régi à une Memory Policy différente.
Un mélange de C++ et de C, mais aucune trace de Cocoa là dedans, donc c'est hors du débat sur les mécanismes de gestion mémoire Cocoa avec leur retain, release, alloc, init, etc :P
Et c'est bien pour ça que ce mécanisme de reference counting et d'autorelease de Cocoa est bien pratique, c'est parce que sans lui donc dans des contextes pur C ou C++ et pas Obj-C avec NSObject et ses mécanismes, est bien pratique, parce que dans des contextes où il n'est pas disponible (comme CoreFoundation utilisé avec du C pur ou avec du C++) on voit bien que c'est vite le boxon (qui a déjà codé en C et C++, sans mécanismes de ReferenceCounting avec AutoreleasePool, sait à quel point c'est vite casse-tête, encore plus dans des environnements multithreadés)
Si MyClass est la mm classe qu'au dessus, au mieux le lien avec le reste du forum c'est que tu utilises CoreFoundation, avec CFRetain et CFRelease, et donc que c'est la CreateRule qui est de mise, mais en aucun cas les règles de gestion mémoire de Cocoa
Et encore, il faut que le programmeur de la classe est bien prévu le truc ...
En java ou C# pas de problème, pareil pour ObjC + garbage collector.
En C++, ça marche pas.
En Cocoa, ça peut foirer !
Pas bon ...
c++ et malloc beurk
sans connaitre le contructeur MyClass(char * buffer), et les methodes buffer() et setBuffer(Char * buffer)
pas grand chose