Tester la libération mémoire d'un objet

ghusseghusse Membre
17:53 modifié dans API AppKit #1
Bonjour,

Je suis débutant en cocoa, j'essaye de créer quelques classes et objets histoire de me faire la main. J'expérimente également les outils permettant les tests unitaires du même coup.

Mon problème, c'est que j'aimerai vérifier dans mes tests unitaires que mes objets sont libérés correctement lorsque j'envoie le message "release". La première idée c'est de vérifier que la valeur des pointeurs est bien à  nil mais ça ne fonctionne pas. Les pointeurs pointent vers des cases mémoires libérées...
J'aimerai vraiment bien vérifier pendant mes tests que mes libérations se font correctement, sans avoir à  passer ma vie sur Instruments.

Est-ce que quelqu'un peu m'aider ?

Merci

Réponses

  • Philippe49Philippe49 Membre
    mars 2008 modifié #2
    Une solution sans doute pas parfaite mais souvent satisfaisante

    Mettre avant @implementation la déclaration d'un compteur d'instances

    static int numberOfInstances=0;
    @implementation MyClass
    ...

    Dans toutes les méthodes d'initialisation, mettre
    numberOfInstances++;
    NSLog(@Allocation. nombre d'instances: %d, numberOfInstances);

    en prenant garde à  ce qu'une même initialisation ne provoque qu'un seul ++

    Dans dealloc, mettre
    numberOfInstances--;
    NSLog(@Deallocation. Nombre d'instances: %d, numberOfInstances);

  • Philippe49Philippe49 Membre
    17:53 modifié #3
    On peut compléter en mettant des messages NSLog dans des redéfinitions de release, retain, ..

    - (oneway void)release
    {
      NSLog(@coucou);
      [super release];
    }
  • Philippe49Philippe49 Membre
    17:53 modifié #4
    Bienvenu au nouveau !!

  • AntilogAntilog Membre
    17:53 modifié #5
    Tu peux vérifier avec
    [monObjet retainCount]
    


    Voir http://www.oreillynet.com/pub/a/mac/2003/06/10/memory_mgmt.html
  • AntilogAntilog Membre
    17:53 modifié #6
    dans 1205227823:

    Bienvenu au nouveau !!




    Le crocodile (et bien d'autres) attend le pot d'arrivée!
  • ghusseghusse Membre
    17:53 modifié #7
    Merci pour toutes ces réponses rapides !

    Je vais essayer de répondre dans l'ordre :
    • Compter le nombre d'instances ... boaf. Ca m'oblige à  changer le code juste pour pouvoir le tester. C'est pas ce qu'il y a de plus convainquant
    • Compter les release en les logguant ne m'intéresse pas, car j'ai besoin de faire des tests automatiques. C'est à  dire du code qui se déroule et qui me dit si oui ou non un test est passé. Pour info : voir "Introduction to Unit Testing Guide" dans la doc XCode.
    • J'ai essayé retainCount, le problème c'est que cet appel plante quand l'objet a effectivement été libéré puisque le pointeur pointe sur une adresse non réservée (BAD ACCESS)...



    Par contre, je crois que ça devrait pouvoir se faire en utilisant STAssertThrows(). C'est une assertion permettant de tester qu'un accès lève une exception.
    Donc en essayant d'accéder à  une propriété de l'objet tel que retainCount, on peut tester si une exception est levée (comme BAD ACCESS) non ?
  • schlumschlum Membre
    17:53 modifié #8
    "ObjectAlloc" dans les "Performance Tools" des outils dévelopeurs...
  • ghusseghusse Membre
    17:53 modifié #9
    Comme je disais précédemment, j'ai besoin de tester automatiquement la désallocation des objets. J'ai besoin de le faire dans des tests unitaires de certaines de mes classes.

    Cela signifie que j'ai besoin de quelque chose, dans du code, qui une fois appelé me dit OK ou KO...

    Je sais que performance tool permet de traquer les fuites mémoires. C'est un outil pour avoir un aperçu global et détecter les fuites une fois l'appli bouclée. Moi, j'ai besoin d'un test unitaire et les outils Apple Instruments ne correspondent pas à  mes besoins.

    J'aurais juste besoin de tester si un objet est bien désalloué correctement. Ma connaissance du cocoa est trop évasive pour savoir comment c'est faisable.

    Sinon, j'ai testé l'utilisation de STAssertThrows([objet retainCount],@Normalement libéré). ça ne fonctionne pas : en fait ce n'est pas une exception qui est levée, c'est carrément le programme qui plante en essayant d'accéder à  une adresse mémoire libérée.
  • Philippe49Philippe49 Membre
    17:53 modifié #10
    dans 1205242150:

    J'aurais juste besoin de tester si un objet est bien désalloué correctement. Ma connaissance du cocoa est trop évasive pour savoir comment c'est faisable.


    ben voir plus haut.
    Ca fait 4-5 lignes de codes, éventuellement à  placer entre des #ifdef DEBUG #endif si on veut les supprimer rapidement.
  • Philippe49Philippe49 Membre
    17:53 modifié #11
    Il y a peut-être quelque chose à  faire du côté NSAssert ... je n'ai jamais fouillé de ce côté
  • schlumschlum Membre
    17:53 modifié #12
    Tester le "retainCount", c'est bien joli, mais en accédant au retainCount d'un objet déjà  désalloué, c'est sûr que ça fait du seg fault...

    Il n'y a pas de méthode miracle pour tester une bonne gestion de la mémoire. C'est du ObjectAlloc, du MallocDebug...
    Le plus compliqué en général, c'est pas de gérer la mémoire de ses propres objets ; ça c'est peanuts.
  • Philippe49Philippe49 Membre
    mars 2008 modifié #13
    dans 1205243522:

    Tester le "retainCount", c'est bien joli, mais en accédant au retainCount d'un objet déjà  désalloué, c'est sûr que ça fait du seg fault...


    D'autant plus que la synchronisation du release n'est pas assurée.
    Le retainCount est souvent (momentanément) supérieur à  ce que l'on attend.

    <br />#include &lt;Foundation/Foundation.h&gt;<br />int main(int argc, char**argv){<br />	NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];<br />	NSString * str=[[NSString alloc] initWithFormat:@&quot;Cocoa is fun in %d&quot;,2008];<br />	NSLog(@&quot;%u&quot;,[str retainCount]); <br />	[str release];<br />	NSLog(@&quot;%u&quot;,[str retainCount]); <br />	[pool release];<br />	return 0;<br />}
    


    Essai : % gcc pgm.m -o pgm -framework Foundation
    Affichage : 1 1

    l'exécution du [str release] a une priorité plus faible que celle de la suite.
  • ghusseghusse Membre
    17:53 modifié #14
    dans 1205243522:

    Il n'y a pas de méthode miracle pour tester une bonne gestion de la mémoire. C'est du ObjectAlloc, du MallocDebug...
    Le plus compliqué en général, c'est pas de gérer la mémoire de ses propres objets ; ça c'est peanuts.


    Je suis assez monomaniaque des tests unitaires, voire XP. Ce qui signifie en théorie que du code n'est écrit que parce qu'un test échoue. Si je ne peux pas tester le fait que mes données sont correctement libérées ...
    Effectivement, peut-être que l'incrémentation/décrémentation d'un compteur entre #if DEBUG #endif suffirait, pour mes propres objets.

    Maintenant, mes objets contiennent des propriétés NSString*, comment tester que les ressources sont bien libérées aussi sur ces objets ?
  • Philippe49Philippe49 Membre
    mars 2008 modifié #15
    try ... catch .. finally

    <br /><br />#include &lt;Foundation/Foundation.h&gt;<br />int main(int argc, char**argv){<br />	NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];<br />	NSString * str=[[NSString alloc] initWithFormat:@&quot;Cocoa is fun in %d&quot;,2008];<br />	NSLog(@&quot;%u&quot;,[str retainCount]); <br /><br />	[str release];<br />@try {<br />    [str release];<br />}<br />@catch (NSException *exception) {<br />    NSLog(@&quot;main: Caught %@: %@&quot;, [exception name], [exception  reason]);<br />}<br />@finally {<br />    NSLog(@&quot;finally&quot;);<br />}<br /><br />	[pool release];<br />	return 0;<br />}
    


    Résultat :
    % pgm
    2008-03-11 17:38:50.904 pgm[992:10b] 1
    pgm(992) malloc: *** error for object 0x105450: double free
    *** set a breakpoint in malloc_error_break to debug
    2008-03-11 17:38:50.907 pgm[992:10b] finally
    %



  • ghusseghusse Membre
    17:53 modifié #16
    J'ai pas essayé, mais effectivement ça me parait être tout à  fait ce que je veux !

    Merci beaucoup !
  • ghusseghusse Membre
    17:53 modifié #17
    J'ai essayé ça :

    BOOL released = false;<br />	<br />	@try{<br />		[maLettre release];<br />	}<br />	@catch (NSException *e) {<br />		released = true;<br />	}<br />	<br />	STAssertTrue(released,@&quot;Case mémoire correctement libérée&quot;);<br />	released = false;<br />	<br />
    


    Et ça ne fonctionne pas. Le script de test (car les tests sont lancés après la compilation de manière automatique par un script) renvoie un FAIL.
    Il s'arrête sur cette ligne :
    <br /> Fail 389 &quot;Test rig &#39;${TEST_RIG}&#39; exited abnormally with code ${TEST_RIG_RESULT} (it may have crashed).&quot;<br />
    
  • mars 2008 modifié #18
    Si tu veux vérifier que tes objects sont releasés du peux par exemple mettre un NSLog dans le dealloc

    - (void)dealloc
    {
    NSLog(@dealloc de %@",self);
    ...
    ...
    [super dealloc];
    }

  • ghusseghusse Membre
    17:53 modifié #19
    Comme je le disais, j'ai besoin d'écrire un test qui me répond par OK/KO. Il ne faut pas que j'ai à  éplucher les logs pour compter si le nombre de release correspond.
  • 17:53 modifié #20
    Comme je le disais le message s'affichera juste avant que l'object soit détruit, c'est à  dire quand ton test est vrai !
Connectez-vous ou Inscrivez-vous pour répondre.