Gestion de memoire

Paisible.frPaisible.fr Membre
06:22 modifié dans API AppKit #1
Soit la classe  :

<br />@implementation immeuble<br />- (id)init<br />{<br />	NSLog(@&quot;construction de l&#39;immeuble&quot;);<br /><br />	return self;<br />}<br /><br />- (id)dealloc<br />{<br />	NSLog(@&quot;destruction de l&#39;immeuble&quot;);<br /><br />	[super dealloc];<br />	return self;<br />}<br /><br />- (void)setName:(NSString *) aName<br />{<br />	NSLog(@&quot;Affection d&#39;un nom a l&#39;immeuble&quot;);<br />	mImmeubleName = aName;<br />}<br />- (NSString *)getName<br />{<br />	return mImmeubleName;<br />}<br /><br />@end<br />


et le code suivant :
<br />&nbsp; &nbsp; &nbsp;  .....<br />&nbsp; &nbsp; &nbsp;  Immeuble *unImmeuble;<br />	<br />	unImmeuble = [[Immeuble alloc] init];<br />	[unImmeuble setName:@&quot;Apple one infinite loop&quot;];<br />	[mImmeubleList addObject:unImmeuble];<br />	<br />	[unImmeuble release];<br />	unImmeuble = nil;<br />	<br />	if (1==1)<br />	{<br />		unImmeuble = [[Immeuble alloc] init];<br />		[unImmeuble setName:@&quot;Microsoft redmond&quot;];<br />		[mImmeubleList addObject:unImmeuble];<br />		<br />		[unImmeuble autorelease];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; unImmeuble = nil;<br />	}<br />&nbsp; &nbsp; &nbsp;  .....<br />


Lorsque je construit l'immeuble d'Apple j'ai bien l'enchainement de "init, setName et dealloc".
Par contre quand je construit l'immeuble microsoft je n'ai pas le dealloc.

Pourquoi ?
«1

Réponses

  • Philippe49Philippe49 Membre
    août 2008 modifié #2
    Tes instances d'immeuble ne sont pas initialisées via [super init]


    - (id)init
    {
    NSLog(@construction de l'immeuble);

    return self=[super init];
    }

    Mais ce n'est pas grave car la doc dit :

    An object isn't ready to be used until it has been initialized. The init method defined in the NSObject class does no initialization; it simply returns self.



    Normalement c'est -(void) dealloc
  • Philippe49Philippe49 Membre
    août 2008 modifié #3
    Après essai, j'ai les deux destructions, mais je préférerais que Microsoft s'écroule avant ...

    % pgm
    2008-08-30 18:28:33.076 pgm[1103:10b] construction de l'immeuble
    2008-08-30 18:28:33.077 pgm[1103:10b] 0x103260
    2008-08-30 18:28:33.078 pgm[1103:10b] Affection d'un nom a l'immeuble
    2008-08-30 18:28:33.079 pgm[1103:10b] construction de l'immeuble
    2008-08-30 18:28:33.079 pgm[1103:10b] 0x1052a0
    2008-08-30 18:28:33.080 pgm[1103:10b] Affection d'un nom a l'immeuble
    2008-08-30 18:28:33.081 pgm[1103:10b] destruction de l'immeuble : Apple one infinite loop
    2008-08-30 18:28:33.081 pgm[1103:10b] destruction de l'immeuble : Microsoft redmond
    %
    #import &lt;Foundation/Foundation.h&gt;<br />@interface Immeuble:NSObject {<br />	NSString * mImmeubleName;<br />}<br />@end<br /><br />@implementation Immeuble<br />- (id)init<br />{<br />	NSLog(@&quot;construction de l&#39;immeuble&quot;);<br />	<br />	return self;<br />}<br /><br />- (void)dealloc<br />{<br />	NSLog(@&quot;destruction de l&#39;immeuble : %@ &quot;,mImmeubleName);<br />	<br />	[super dealloc];<br />}<br /><br />- (void)setName:(NSString *) aName<br />{<br />	NSLog(@&quot;Affection d&#39;un nom a l&#39;immeuble&quot;);<br />	mImmeubleName = aName;<br />}<br />- (NSString *)name<br />{<br />	return mImmeubleName;<br />}<br /><br />@end<br />int main (int argc, const char * argv&#91;]) {<br />	NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];<br />	<br />	Immeuble *unImmeuble;<br />	NSMutableArray * mImmeubleList=[NSMutableArray array];<br />	unImmeuble = [[Immeuble alloc] init];<br />	NSLog(@&quot;%p&quot;,unImmeuble);<br />	[unImmeuble setName:@&quot;Apple one infinite loop&quot;];<br />	[mImmeubleList addObject:unImmeuble];<br />	<br />	[unImmeuble release];<br />	unImmeuble = nil;<br />	<br />	if (1==1)<br />	{<br />		unImmeuble = [[Immeuble alloc] init];<br />		NSLog(@&quot;%p&quot;,unImmeuble);<br />		[unImmeuble setName:@&quot;Microsoft redmond&quot;];<br />		[mImmeubleList addObject:unImmeuble];<br />		<br />		[unImmeuble autorelease];<br />		unImmeuble = nil;<br />	}<br />	[pool drain];<br />	return 0;<br />}
    







  • ChachaChacha Membre
    août 2008 modifié #4
    Effectivement, ton code ne respecte pas les règles habituelles en Objective-C/Cocoa (il y a même une fuite mémoire dans le setter).
    Mais pour répondre précisément à  ta question :
    il y a, bien entendu, une différence entre release et autorelease.
    -autorelease, c'est comme envoyer release, mais le release sera fait "un peu plus tard".
    -"un peu plus tard" ne veut pas dire n'importe quand
    -en réalité, "autorelease" inscrit ton objet au bassin d'autorelease (autorelease pool). Quand ce bassin sera supprimé, ton objet recevra le release (et sera donc supprimé si c'est son "dernier" release)
    -Si tu ne vois pas d'autoreleasepool dans ton code, c'est que Cocoa en crée automatiquement un pour toi dans la boucle événementielle. Au prochain "passage" dans la boucle d'interface, le bassin sera supprimé et recréé
    -tu peux très bien créer toi même tes bassins d'autorelease; [[NSAutoreleasePool alloc] init]. Il n'y a rien de particulier à  faire pour choisir le bassin dans lequel ton objet sera inscrit : il est automatiquement mis dans le dernier bassin créé.
    Essaye cela :

    NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
    unImmeuble = [[Immeuble alloc] init];
    [unImmeuble autorelease];
    [ap release];


    Un truc m'étonne : si tu mes tes immeubles dans mImmeubleList, ce dernier devrait leur faire un "retain", et donc tes objets resteraient forcément en vie tant que mImmeubleList existe.

    +
    Chacha
  • Philippe49Philippe49 Membre
    06:22 modifié #5
    Oui le moment où l'autorelease est effectué est aléatoire, toujours après la fin du bloc d'instructions définissant la variable, ou si un autoreleasepool a été créé, à  la destruction de celui-ci.
  • schlumschlum Membre
    06:22 modifié #6
    dans 1220114704:

    Oui le moment où l'autorelease est effectué est aléatoire, toujours après la fin du bloc d'instructions définissant la variable, ou si un autoreleasepool a été créé, à  la destruction de celui-ci.


    Hein ? c'est pas aléatoire du tout... c'est à  un moment bien précis  :P
    - Soit au "release" d'un NSAutoreleasePool perso
    - Soit à  la fin (début ?) du cycle de runloop
  • Philippe49Philippe49 Membre
    août 2008 modifié #7
    dans 1220123056:

    Hein ? c'est pas aléatoire du tout... c'est à  un moment bien précis  :P
    - Soit au "release" d'un NSAutoreleasePool perso
    - Soit à  la fin (début ?) du cycle de runloop


    Il y a bien d'autre cas que ceux-là . Si une variable est en autorelease dans une méthode qui ne contient pas de NSAutoreleasePool  par exemple, elle est libérée bien avant la fin du run loop, et heureusement ! et là  c'est une question de priorité qui n'est pas aléatoire au sens pur du terme certes, mais qui n'est pas gérée par le programme.
  • schlumschlum Membre
    06:22 modifié #8
    Il ne s'agit pas de fin de la runloop ! Mais de cycle !!

    Elle ne sera pas libérée avant la fin du cycle de runloop  ;)
    Et c'est parfaitement contrôlé par le programme  :P

    Heureusement que c'est pas aléatoire sinon on aurait de sacrés problèmes  :o

    La fin du cycle de runloop est un moment stratégique... on a fini tout ce qu'on avait à  faire pour le cycle en cours, donc on n'a plus besoin des variables autoreleased ; on entame un nouveau cycle tout neuf  :)
  • Philippe49Philippe49 Membre
    06:22 modifié #9
    dans 1220129160:

    Il ne s'agit pas de fin de la runloop ! Mais de cycle !!

    Elle ne sera pas libérée avant la fin du cycle de runloop  ;)
    Et c'est parfaitement contrôlé par le programme  :P

    Ce n'est pas l'impression que j'ai eu lors de l'observation du phénomène.
    Si une méthode A reçoit d'une autre méthode B une variable qui est en autorelease dans B, elle continue à  l'utiliser.  Pour cela elle fait une sorte de retain-autorelease sur cette variable, (son retaincount est en général de valeur=2), etc ...
    Et à  la fin de la seconde méthode peut-être qu'une troisième méthode C sera enclanchée,n'utilisant pas cette variable. Qu'est-ce qui décide alors du déclenchement des release, rien ne presse, et en (bon?) programmeur j'aurais tendance à  faire l'urgent plutôt que le secondaire.
    Sa durée de vie est donc quelque part indéterminée, on sait simplement qu'elle vit le temps de son utilisation dans le programme.
    Quand disparaà®t-elle, on n'en sait rien. Dès que faire se peut sans doute, je pense qu'un thread du programme se charge du nettoyage des autoreleasePool.

    Rien ne permet de dire qu'à  un instant donné la zone mémoire qu'occupait une variable en autorelease est libérée. Ce n'est pas un free(). Du moins, je n'ai jamais lu un tel engagement de la part d'Objective-C.

    dans 1220129160:

    Heureusement que c'est pas aléatoire sinon on aurait de sacrés problèmes  :o

    On peut considérer qu'il y a une durée de vie minimale assurée, et qu'après c'est aléatoire.
    Bref, la retraite à  65 ans, après vous verrez ... :)


    Maintenant, je veux bien voir une doc quelque part disant quelque chose de plus précis que ce que j'avance.
  • schlumschlum Membre
    août 2008 modifié #10
    Non non, c'est écrit très clairement dans la doc  ;)
    http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Concepts/AutoreleasePools.html#//apple_ref/doc/uid/20000047-CJBFBEDI

    The Application Kit automatically creates a pool at the beginning of an event cycle (or event-loop iteration), such as a mouse down event, and releases it at the end, so your code normally does not have to worry about them.


    Donc en fait plus précisément par rapport à  ce que j'ai dit au dessus, au niveau de la run-loop, quand un événement doit être traité, un NSAutoreleasePool est créé, puis détruit à  la fin de l'événement (ce qui revient au même que dire qu'il est " drainé " à  la fin de la boucle d'événements).
  • Philippe49Philippe49 Membre
    août 2008 modifié #11
    On ne constate pas cela avec Instruments.
    Sur une simple application j'ai deux ou trois NSAutoreleasePool qui ont été créés, dès le début, c'est tout.


    Ce que je lis dans la citation : c'est "on s'en occupe", mais cela ne dit pas comment. Pour moi l'Event-Cycle dure toute la durée de vie, pour une application simple tout du moins.
  • schlumschlum Membre
    06:22 modifié #12
    Tu peux relire Cocoa par la Pratique page 60 aussi  :P

    " Dans le cadre des applications Cocoa, le pool de libération automatique est créé avant la gestion de l'événement concerné et il est libéré après. Par conséquent, à  moins que les objets du pool de libération automatique ne soient conservés, ils seront détruits dès que l'événement aura été géré. "

    Jamais d'aléatoire dans la gestion des NSAutoreleasePool, sauf peut-être dans le cadre de l'utilisation du Garbage Collector avec Obj-C 2.0  ???
  • Philippe49Philippe49 Membre
    06:22 modifié #13
    Avec Instruments je n'ai constaté que 3 NSAutoreleasePool créés, lors d'une utilisation prolongée, avec de nombreuses événements créés, et de nombreuses méthodes appelées.

    Dans la troisième édition de Hillegass, il dit : "Objects are added to the autorelease pool when they are sent the message autorelease. When the pool is drained, it sends the message release to all objects in the pool. In other words, when an object is autoreleased, it is marked to be sent release sometime in the future"
    Après il rajoute la phrase que tu as signalée, qui semble s'appliquer aux events, mais je ne vois pas trop comment cela s'articule avec le programme que l'on fait.



    dans 1220133201:

    Jamais d'aléatoire dans la gestion des NSAutoreleasePool, sauf peut-être dans le cadre de l'utilisation du Garbage Collector avec Obj-C 2.0  ???

    Jamais d'aléatoire lorsque l'on fait [pool drain] ou [pool release], mais quand le pool principal de l'application se libère au fur et à  mesure, on sait à  partir de quand il peut le faire, mais on ne sait pas quand il le fait. Cela n'est pas en contradiction avec les citations que tu donnes.

    En mode debug, on peut donc croire à  l'instant t+10 qu'un objet existe encore, alors qu'il est dans le pool à  l'instant t, mais non encore détruit.
  • schlumschlum Membre
    06:22 modifié #14
    Un pool ne peut être drainé que depuis Obj-C 2.0...
    Je pense que l'aléatoire dont tu parles provient du garbage collector.
  • Philippe49Philippe49 Membre
    06:22 modifié #15
    Je n'utilise pas GC
    Mettre un breakpoint sur la méthode drain de NSAutoreleasePool est également une expérience intéressante, éprouvante pour les nerfs, mais intéressante.
  • schlumschlum Membre
    août 2008 modifié #16
    Je n'utilise pas Obj-C 2.0... pas de drain.

    Mais même avec "drain" sans GC, ça m'étonnerait fortement que ça draine aléatoirement.

    Il y a forcément un mécanisme logique (sinon c'est plus un langage structuré, c'est du grand n'importe quoi !)
  • Philippe49Philippe49 Membre
    06:22 modifié #17
    dans 1220140913:

    Il y a forcément un mécanisme logique (sinon c'est plus un langage structuré, c'est du grand n'importe quoi !)

    On est d'accord.
    Quand je parle d'aléatoire, je pense au modèle de deux threads lancés en concurrence. Les deux fonctionnent, mais le résultat est rarement le même. C'est structuré, mais le résultat n'est pas déterminé à  l'avance.

    On peut aussi penser à  un modèle plus déterministe,  comme celui d'un mécanisme de vidage de tampon classique :
    • A des moments précis, drain est effectué sur l'autoreleasePool (sans doute sur une boucle d'événements).
    • A partir d'un certain degré de remplissage, un drain est effectué sur l'autoreleasePool.

    Celui du thread avec une faible priorité est peut-être plus souple à  l'utilisation ...
  • schlumschlum Membre
    06:22 modifié #18
    Chaque thread a des pools distincts... Il ne faut pas partager les objets autoreleased entre threads !
  • Philippe49Philippe49 Membre
    06:22 modifié #19
    En C, il n'y a rien qui l'impose donc la gestion générale de l'appli peut s'affranchir de cette contrainte typiquement Obj-C.
    De toutes façons, dans le pool les données sont considérées comme de la mémoire à  libérer, et il ne peut y avoir interférence entre un thread gérant la libération du pool commun  et un autre thread du programme possédant son propre pool, géré lui-même par un système analogue, sauf erreur de programmation.

    J'ai l'impression que tu as en tête un modèle comme quoi à  chaque bloc d'instructions un pool est créé, avec drain à  la fin du bloc. Par exemple, 1000 pool serait créé pour la boucle
    for(i=0;i<1000;i++) {
      NSString * string=[NSString stringWithFormat:... ];
      ...
    }
    Cela me semble impossible.

    De même, pour une unité plus grande, une méthode ou une boucle d'événements : c'est trop ou pas assez, c'est au programmeur de décider si il veut le faire ou pas.
    Ce qui est possible (voire probable) c'est qu'à  la fin de la gestion d'un événement, il y ait un drain exécuté sur le pool concerné.

  • schlumschlum Membre
    06:22 modifié #20
    Attention à  ne pas confondre C et Obj-C... Les autoreleasePool c'est typiquement un truc Obj-C, il n'y a aucun équivalent en C.
    Donc non, il ne faut pas s'affranchir de cette contrainte  :o
    Les objets partagés doivent être des objets alloués par la voie normale !

    Je n'ai jamais dit que chaque scope devait avoir un pool (encore heureux !) par contre, si ta boucle d'itération est très consommatrice de mémoire autoreleased, utile uniquement pour une itération, oui elle doit comporter un NSAutoreleasePool !

    Encore une fois, le drain c'est que pour Obj-C 2.0 on parle d'un principe général là ...
    (et si la doc n'a pas été mise à  jour, c'est que ça doit fonctionner pareil...)
  • ChachaChacha Membre
    06:22 modifié #22
    Philippe49, je ne comprends pas où tu veux en venir.
    Schlum explique comment sont censés être utilisés les AutoreleasePool. Il doit effectivement y en avoir au moins un par thread. (cf. la documentation de Cocoa, comme detachDrawingThread:toTarget:withObject:).
    Après, il est aussi possible de faire n'importe quoi avec un langage en mélangeant les objets entre les autorelease pools, mais je n'en vois pas l'intérêt. (c'est d'ailleurs une des difficultés que règlent en partie les fameuses properties de Obj-C 2.0)


    Par exemple, 1000 pool serait créé pour la boucle
    for(i=0;i<1000;i++) {
      NSString * string=[NSString stringWithFormat:... ];
      ...
    }
    Cela me semble impossible.

    Effectivement, ils ne sont pas créés tous seuls, mais rien n'empêche d'écrire


    NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
    for(i=0;i<1000;i++) {
     NSString * string=[NSString stringWithFormat:... ];
     ...
    }
    [ap release];//garantit que les string sont détruites ici.



    Ce qui est possible (voire probable) c'est qu'à  la fin de la gestion d'un événement, il y ait un drain exécuté sur le pool concerné.

    Oui, c'est ce qu'on dit : un autoreleasePool est créé à  chaque runloop.

    Et quand tu parles d'aléatoire : oui, l'ordre des instructions de deux threads concurrents sont plus ou moins aléatoires, mais au sein d'un thread, la gestion de la mémoire n'est pas plus aléatoire qu'ailleurs. C'est un autre débat.

    +
    Chacha
  • Philippe49Philippe49 Membre
    06:22 modifié #23
    dans 1220179915:

    Encore une fois, le drain c'est que pour Obj-C 2.0 on parle d'un principe général là ...
    (et si la doc n'a pas été mise à  jour, c'est que ça doit fonctionner pareil...)

    Le drain existait avant, forcément, il n'était pas documenté, c'est tout.

    dans 1220179915:

    Attention à  ne pas confondre C et Obj-C... Les autoreleasePool c'est typiquement un truc Obj-C, il n'y a aucun équivalent en C.

    Je ne vois pas le rapport avec le fait qu'on cherche à  savoir le fonctionnement arrière des applications. 
    Je ne cherche pas à  savoir comment utiliser un autoreleasepool, je me pose des questions sur la durée de vie de l'allocation d'une zone mémoire quand sans avoir créé un seul pool, je fais un autorelease sur une variable. 

    Un peu plus haut je présente deux modèles possibles, l'un de libération par un pthread, pas du tout objective-C, et un autre par une méthode de vidage de tampon comme on peut le rencontrer classiquement en C.

  • schlumschlum Membre
    06:22 modifié #24
    dans 1220180855:

    Je ne cherche pas à  savoir comment utiliser un autoreleasepool, je me pose des questions sur la durée de vie de l'allocation d'une zone mémoire quand sans avoir créé un seul pool, je fais un autorelease sur une variable. 


    La réponse et très simple et expliquée dans la doc :
    - Warning d'erreur
    - Leak


    Quant à  drain, non il n'existait même pas en non documenté  ;)
  • Philippe49Philippe49 Membre
    06:22 modifié #25
    dans 1220181069:

    Quant à  drain, non il n'existait même pas en non documenté  ;)

    Cela existait forcément, peut-être sous un autre nom, pour vider le pool sans le détruire.
    Et ce n'était pas documenté.
    Confère ce post

  • schlumschlum Membre
    06:22 modifié #26
    10.4 only... Et pas utilisé  :P
    Je pense que c'était en préparation pour 10.5...

    Pourquoi voudrais-tu vider le pool sans le détruire ?
    L'utilisation de NSAutoreleasePool ne prévoyait pas cette utilisation...  ;)
  • schlumschlum Membre
    06:22 modifié #27
    Je ne comprends pas ce que tu essaies de prouver exactement... L'utilisation des NSAutorelasePool par le programme est décrit en détails dans la doc.

    Les seuls gérés par Cocoa sont ceux des boucles d'événements, créés au début et détruits à  la fin ; le reste est géré par l'utilisateur.
    Où est la surprise là  dedans ?  ???
  • Philippe49Philippe49 Membre
    06:22 modifié #28
    Parce que comme dit plus haut je ne vois dans Instruments que 3 autoreleasePool de créés, et qui reste à  la même adresse.
    C'est donc le même pool qui est utilisé , et vidé plusieurs fois comme le breakpoint posé sur la méthode drain le montre.
  • schlumschlum Membre
    06:22 modifié #29
    dans 1220184595:

    Parce que comme dit plus haut je ne vois dans Instruments que 3 autoreleasePool de créés, et qui reste à  la même adresse.
    C'est donc le même pool qui est utilisé , et vidé plusieurs fois comme le breakpoint posé sur la méthode drain le montre.


    C'est parce qu'avec Obj-C 2.0, ils ont dû remplacer la destruction / création par un drain sur celui du thread, au même niveau, vala tout  ???
  • Philippe49Philippe49 Membre
    06:22 modifié #30
    dans 1220184467:

    Je ne comprends pas ce que tu essaies de prouver exactement... L'utilisation des NSAutorelasePool par le programme est décrit en détails dans la doc.

    Les seuls gérés par Cocoa sont ceux des boucles d'événements, créés au début et détruits à  la fin ; le reste est géré par l'utilisateur.
    Où est la surprise là  dedans ?  ???

    Si à  chaque événement un pool était créé, cela se verrait dans Instruments > Object Alloc

    Pour moi un pool global est créé au lancement de l'application, et peut-être un nombre limité d'autres pools pour différents détails ou threads non créés par le programme mais nécessaires au bon fonctionnement de l'appli. Et quand on fait autorelease, sans avoir créé son propre pool, la variable est inscrite , "marked" dit Hillegass, comme susceptible de recevoir un release dans un futur "imprécis".
  • Philippe49Philippe49 Membre
    06:22 modifié #31
    dans 1220184863:

    dans 1220184595:

    Parce que comme dit plus haut je ne vois dans Instruments que 3 autoreleasePool de créés, et qui reste à  la même adresse.
    C'est donc le même pool qui est utilisé , et vidé plusieurs fois comme le breakpoint posé sur la méthode drain le montre.


    C'est parce qu'avec Obj-C 2.0, ils ont dû remplacer la destruction / création par un drain sur celui du thread, au même niveau, vala tout  ???


    Ben oui, mais je me tue à  le dire depuis je ne sais combien de lignes  :'(


Connectez-vous ou Inscrivez-vous pour répondre.