Alloc, retain, release et autorealease

LeChatNoirLeChatNoir Membre, Modérateur
16:43 modifié dans API AppKit #1
Salut,

Question récurrente sur la gestion mémoire...

J'ai cru comprendre que quand on fait un alloc, y a automatiquement un retain qui est fait sur l'objet.
J'avais un objet sur lequel je faisais [[[monobjet alloc] init] retain] et il fallait faire 2 release pour effectivement le libérer.
Je fais donc un "alloc init" tout court et un seul release.

Question : est-ce que j'ai bien tout compris ou est ce parce qu'il y a un autre endroit ou il y a un retain sur mon objet ?

Ensuite, une question sur les NSEnumerator.
Quand on parcours un tableau avec nextObject. Ce fait un retain sur les objets à  chaque fois ?
J'ai l'impression que oui mais je n'en suis pas sûr...

C'était la 2eme question.
Merci !

Réponses

  • 16:43 modifié #2
    alloc+init suffit.

    Pour le NSEnumerator non, car un énumérateur et un objet utilisé pour parcourir une collection. Et tous les objets contenus dans la collection sont 'retenus' au moins par elle. Donc tant que la collection originale existe (ce qui devrait être le cas pendant ta boucle) les objets existeront.
  • BruBru Membre
    16:43 modifié #3
    dans 1127206298:

    J'ai cru comprendre que quand on fait un alloc, y a automatiquement un retain qui est fait sur l'objet.
    J'avais un objet sur lequel je faisais [[[monobjet alloc] init] retain] et il fallait faire 2 release pour effectivement le libérer.
    Je fais donc un "alloc init" tout court et un seul release.
    Question : est-ce que j'ai bien tout compris ou est ce parce qu'il y a un autre endroit ou il y a un retain sur mon objet ?


    Chaque objet contient un "compteur" de référence. Ce compteur s'incrémente de 1 à  chaque retain, et se décrémente de 1 à  chaque release.
    Lorsque le compteur tombe à  0, le runtime appelle la méthode dealloc, et l'objet est détruit.

    Un alloc/init initialise toujours l'objet avec un compteur à  1 : il n'est donc pas nécessaire de faire un retain.

    L'autorelease pool est un objet qui contient une liste de tous les objets qui ont eu un message autorelease. Lorsque l'autoreleasepool va être détruit, il va envoyer un message release à  tous les objets de cette liste. Les objets qui avaient leur compteur à  1 se voient donc eux même détruits.

    .
  • AntilogAntilog Membre
    16:43 modifié #4
    Pour y voir plus clair, tu peux vérifier:
    NSLog(@"retainCount : %d", [myObject retainCount]);
    


    A noter que les autoreleases non encore 'réellement' effectués ne sont pas pris en compte...
  • LeChatNoirLeChatNoir Membre, Modérateur
    16:43 modifié #5
    Ah c'est cool ça ! Bien pratique !

    Parce qu'il y a un truc que je pige pas.
    J'ai un objet avec relation vers un père et un tableau de pointeurs vers des fils (permet de faire un arbre).

    Or quand on fait un addChild, ca fait un retain sur le child en question.

    Et un removeChild, vu qu'on le remove du tableau, l'objet reçoit un release.

    Or, je veux intercaler un objet entre un père et ses fils. Donc je fais une boucle sur tous les fils via un Enumerator, je fais un remove du père et un addChild sur mon objet à  intercaler.

    Donc, ca devrait pas marcher vu que le remove fait un release sur l'objet...

    Bref, je sais pas si je suis très clair donc je vais décortiquer le truc ce soir avec la super combine d'Antilog (merci !)
  • AntilogAntilog Membre
    16:43 modifié #6
    Je n'ai pas tout saisi de ton implémentation  ??? , mais...

    Au cas où, saches qu'il est fortement déconseillé de supprimer un objet d'une collection parcourue par un énumérateur (je ne suis pas certain que ce soit ce que tu fait, mais il vaut mieux être prudent  ;)).
  • LeChatNoirLeChatNoir Membre, Modérateur
    16:43 modifié #7
    Apparemment, un Enumerator fait un retain sur la collection (donc un retain est surement envoyé à  tous les objets) le temps du parcours de la collection.

    J'imagine qu'il envoit un release dès qu'on atteint la fin de l'enumération.

    Ci-dessous, la phrase qui me fait dire ça (doc Apple) :
    The enumerator subclasses used by NSArray, NSDictionary, and NSSet retain the collection during enumeration. When the enumeration is exhausted, the collection is released.

    Bon mais de ttes façons, en utilisant ton astuce, j'ai vu que mes objets avaient un retainCount à  2/3 ou 4 selon leur position dans la hiérarchie !
    J'ai donc pas tout compris et il faudrait que je passe en debug pour voir où sont fait les retain.
    Mais j'ai pas mon portable today so :-(

    a+
  • AntilogAntilog Membre
    16:43 modifié #8
    Le problème n'est pas au niveau de l'objet lui-même et de son retainCount.

    Si tu envoie un removeObject: a un objet dans une collection, la collection se sépare de cet objet, et le "compteur" NSEnumerator ne se trouve plus positionné correctement dans la collection.

    Référence : http://developer.apple.com/documentation/Cocoa/Reference/Foundation/ObjC_classic/Classes/NSEnumerator.html
  • LeChatNoirLeChatNoir Membre, Modérateur
    16:43 modifié #9
    Salut,

    J'ai un peu avancé et j'ai l'impression que le NSEnumerator brouille les pistes.

    En effet, je l'utilise pour parcourir mon arbre d'objets (les fils d'un objet sont accessibles grâce à  un tableau de pointeurs sur ces fils) et afficher les retainCount.

    Or je crois bien que les retain sont incrémentés par le NSEnumerator !
    Puis releasé ensuite.

    Du coup, ma tentative de debug est pas bonne.

    Il faut que je le parcours manuellement et là , les retain sont cohérents !

    Bref, j'ai pas encore tout expliqué mais je pense que cette piste est la bonne.

    a+




  • AliGatorAliGator Membre, Modérateur
    16:43 modifié #10
    A la limite pendant ton parcours avec ton NSEnumerator*, pour chaque noeud de ton arbre que tu veux supprimer, au lieu de lui envoyer un release, tu l'ajoute à  un array [tempArray addObject:obj];

    Une fois le parcours effectué, tu envoies un release à  tous les éléments de tempArray (il me semble qu'il y a même une méthode pour envoyer le même message à  tous les éléments d'une collection. Et enfin tu releases ton tempArray;

    Dans ton arbre, tous tes elements auront avant le parcours un retainCount de 1. Quand tu vas mettre les noeuds à  supprimer dans le tableau pour les mettre de côté, ces noeuds auront un retainCount de 2. Ensuite tu envoie un retain à  tous les éléments du tableau, du coup tout le monde redescend à  1, mais les noeuds que tu doit garder dans ton arbre auront un retainCount de 1 parce que "stockés dans l'arbre", et ceux que tu dois supprimer auront un retainCount de 1 parce que "stockés dans le tempArray". Du coup quand tu termines en releasant ton tempArray, tous ceux que tu avais mis dedans ont leur retainCount tombant à  zéro :)

    Du coup le principe est de mettre de côté les éléments à  supprimer au lieu de les supprimer tout de suite, et d'envoyer le retain en groupe une fois l'énumération effectuée.
Connectez-vous ou Inscrivez-vous pour répondre.