Autorelease pool suite

août 2008 modifié dans API AppKit #1
[size=10pt]Bonjour à  tous,

Puisque le sujet est ouvert sur les autorelease pool, j'aimerais éclaircir un point.

Nous savons que lorsqu'un événement est déclenché, la boucle d'événements du windows server construit un objet NSEvent pour caractériser l'événement en question. Elle construit, en autres, un objet autorelease pool pour mémoriser tous les objets construits par la suite en autorelease, elle invoque ensuite la méthode appropriée au niveau de la vue (premier répondeur), le cas échéant. Il doit exister d'autres opérations qui ne sont pas utiles pour le sujet.

Dans ce cas, l'insertion dans le pool semble être effectuée au niveau la méthode autorelease.
Ce qui est troublant est que pour les autres cas, tels que dans les threads, nous soyons obligés de créer notre propre auto release pool afin que les autres objets alloués en autorelease puissent en bénéficier.
Ce qui amène la question suivante:
Comment ces nouveaux objets bénéficient-ils de ce pool de destruction automatique, car aucun lien n'est établit formellement au niveau du code ?

[/size]

Réponses

  • schlumschlum Membre
    13:23 modifié #2
    Le lien doit être fait au niveau du constructeur d'autoreleasePool...
    Regarde la doc, il y a une " pile " de NSAutoreleasePool (correspondant à  des scopes imbriqués).
    Et si par exemple, il y a une exception ces objets sont relâchés quand même.

    Ce qui me laisse à  penser que ces objets sont plutôt de type C++ (alloués sur la pile) que Obj-C.
  • Philippe49Philippe49 Membre
    13:23 modifié #3
    Je voudrais pas insister, mais simplement comprendre.
    L'expérience avec instruments est résumée dans les deux images ci-dessous.

    • On lance l'appli : première image.
    • Je clique sur un segmented control, lançant un événement :deuxième image.

    Ce que je vois c'est que l'événement n'a pas créé d'AutoreleasePool. C'est toujours le même qui sert ... ou alors expliquez ce résultat donné par Instruments.
  • schlumschlum Membre
    août 2008 modifié #4
    Oui, c'est peut-être le même qui sert depuis 10.5 (peut-être même 10.4 qui sait !)...
    Qu'est-ce qui l'empêche d'être drainé à  un moment précis (entre 2 events) ?

    Il est possible qu'ils aient un seul NSAutoreleasePool contrôlant la boucle d'événements maintenant en le drainant à  la fin de chaque événement.

    Mais ça ne change absolument rien par rapport au fait d'en créer un au début et de le détruire après ; ça occasionne les mêmes appels au mêmes moments pour les objets autoreleased  ;)
  • schlumschlum Membre
    août 2008 modifié #5
    En gros, au lieu d'avoir ça :

    while(readevent()) {<br />&nbsp; &nbsp; NSAutoreleasePool *eventPool = [[NSAutoreleasePool alloc] init];<br />&nbsp; &nbsp; // ... event management<br />&nbsp; &nbsp; [eventPool release];<br />}
    


    On a ça :
    while(readevent()) {<br />&nbsp; &nbsp; // ... event management<br />&nbsp; &nbsp; [eventsPool drain];<br />}
    


    ou "eventsPool" est un pool alloué une fois pour toute à  la boucle d'événement...
  • Philippe49Philippe49 Membre
    13:23 modifié #6
    dans 1220196340:

    Oui, c'est peut-être le même qui sert depuis 10.5 (peut-être même 10.4 qui sait !)...
    Qu'est-ce qui l'empêche d'être drainé à  un moment précis (entre 2 events) ?


    Oui c'est ce que je pense après la discussion précédente, et un essai avec un breakpoint sur la méthode drain montre qu'il y a aussi des drain intermédiaires.
  • Philippe49Philippe49 Membre
    13:23 modifié #7
    dans 1220196606:

    En gros, au lieu d'avoir ça :

    while(readevent()) {<br />&nbsp; &nbsp; NSAutoreleasePool *eventPool = [[NSAutoreleasePool alloc] init];<br />&nbsp; &nbsp; // ... event management<br />&nbsp; &nbsp; [eventPool release];<br />}
    


    On a ça :
    while(readevent()) {<br />&nbsp; &nbsp; // ... event management<br />&nbsp; &nbsp; [eventsPool drain];<br />}
    


    ou "eventsPool" est un pool alloué une fois pour toute à  la boucle d'événement...


    Oui c'est ce que je pense, un pool global , une sorte de sharedAutoreleasePool, ce qui est d'ailleurs plus en accord avec la doc disant que drain se traduit par release si le GC n'est pas activé. Dans ce cas, la classe NSAutoreleasePool redéclarerait à  sa sauce le release en un "drain" 
  • schlumschlum Membre
    13:23 modifié #8
    dans 1220196817:

    dans 1220196340:

    Oui, c'est peut-être le même qui sert depuis 10.5 (peut-être même 10.4 qui sait !)...
    Qu'est-ce qui l'empêche d'être drainé à  un moment précis (entre 2 events) ?


    Oui c'est ce que je pense après la discussion précédente, et un essai avec un breakpoint sur la méthode drain montre qu'il y a aussi des drain intermédiaires.



    Les drain intermédiaires, j'y crois pas... ça serait ingérable.
    si on crée un objet autoreleased au début d'un event, on peut avoir envie de l'utiliser à  la fin, je ne vois pas ce qui nous l'interdirait.
  • NoNo Membre
    13:23 modifié #9
    dans 1220188825:

    Nous savons que lorsqu'un événement est déclenché, la boucle d'événements du windows server construit un objet NSEvent pour caractériser l'événement en question.

    Non, nous ne savons rien.
    Les NSQuelqueChose, ou plus généralement les objets, sont des choses complètement inconnues du window server (note que'il n'y a pas de s à  window, sinon, on parlerai de la chose qui tourne sous PC).
    Le window server appelle des fonctions callback. Sous le framework cocoa, ces fonctions callback transforment les structures CGEvent en NSEvent pour les déposer dans la file des événements. Ensuite, c'est la runloop du programme qui va extraire ces événements pour appeler les méthodes Objective-C appropriées.

    Sous 10.4 et antérieur, un autoreleasePool est créé avant l'extraction de l'événement. Après, il y a appel des différentes méthodes de traitement de ce dernier. Enfin, au retour et avant l'extraction du suivant, il y a vidage du autoreleasePool (par destruction).

    Toutefois, en cas de plusieurs événements successifs, il se peut que la destruction du précédent autoreleasePool puis création du nouveau donne le même pointeur (et donc le même objet), du fait de l'alignement des objets sur les pages mémoires (pour augmenter l'efficacité de la pagination).

  • Philippe49Philippe49 Membre
    13:23 modifié #10
    dans 1220199169:

    Toutefois, en cas de plusieurs événements successifs, il se peut que la destruction du précédent autoreleasePool puis création du nouveau donne le même pointeur (et donc le même objet), du fait de l'alignement des objets sur les pages mémoires (pour augmenter l'efficacité de la pagination).

    J'y ai pensé évidemment, mais non, regarde les dates dans les photos d'Intruments, elles auraient changées après un événement.
  • AliGatorAliGator Membre, Modérateur
    13:23 modifié #11
    Peut-être que même depuis 10.4 un AutoreleasePool n'est pas créé à  chaque RunLoop...
    Ce qui n'empêche pas le principe qui existe depuis belle lurette en Objective-C que la Runloop du thread principal a automatiquement son AutoreleasePool, et qu'à  chaque cycle de cette runloop, les objets qui sont dans cet AutoreleasePool reçoivent un message "release"...

    En somme, au lieu comme on peut le penser que la runloop du thread principal simule un [tt]NSAutoreleasePool* mainPool = [[NSAutoreleasePool alloc] init][/tt] au début de son cycle et un [tt][mainPool release[/tt] à  la fin de son cycle (ce qui allouerait une nouvelle pool à  chaque cycle de runloop), ça n'allouais la "mainPool" (privée) seulement une fois en début de programme, et à  chaque fin de cycle de la runloop, ça envoyait un message pour demander de la vider (envoyant un release aux objets dans la pool puis en vidant cette dernière, mais sans envoyer de release à  la pool elle-même qui ne sera donc pas désallouée). Et ça ne faisait le vrai release sur la mainPool qu'à  la fermeture du programme, pour désallouer cette pool avant de quitter.

    Ca serait même logique comme comportement, ça éviterait de faire des allocations et désallocations d'une pool à  chaque cycle de runloop, utilisant la même releasepool pour tous les cycles (se contentant de la vider à  chaque cycle au lieu de déallouer/réallouer), c'est bien plus efficace...
  • Philippe49Philippe49 Membre
    août 2008 modifié #12
    dans 1220199102:

    Les drain intermédiaires, j'y crois pas... ça serait ingérable.
    si on crée un objet autoreleased au début d'un event, on peut avoir envie de l'utiliser à  la fin, je ne vois pas ce qui nous l'interdirait.

    J'ai pas vérifié, mais on peut très bien imaginer un mécanisme de ce type :
    [EDIT] j'ai vérifié, c'est faux


    La durée de vie promise pour une variable en autorelease n'est pas celle de la gestion d'un événement, mais simplement celle du bloc d'instructions/méthode où elle se trouve.
    Après si une autre méthode utilisant cette objet est appelée à  partir de ce bloc, cette méthode fait un retain à  la réception sur l'objet, et un release à  la fin d'exécution de la méthode, ce qui fait que si un drain a eu lieu entre temps, l'objet a reçu un release, mais retainCOunt=1, puis cela revient au même à  la fin de la méthode. Le nouveau release libère l'individu.
    En clair, les méthodes retiennent leurs arguments. Cela explique des retainCount sont souvent surévalués par rapport au code que l'on écrit.
  • ChachaChacha Membre
    août 2008 modifié #13
    dans 1220205412:

    J'ai pas vérifié, mais on peut très bien imaginer un mécanisme de ce type :

    Non, on ne peut pas imaginer ça. Sinon on ne pourrait pas initialiser des variables "hors fonction" (statiques ou variables d'instance) [edit]avec des objets autoreleasés[/edit] dans une méthode !

    [edit]J'avais oublié de finir ma phrase :)
  • Philippe49Philippe49 Membre
    août 2008 modifié #14
    dans 1220207723:

    dans 1220205412:

    J'ai pas vérifié, mais on peut très bien imaginer un mécanisme de ce type :

    Non, on ne peut pas imaginer ça. Sinon on ne pourrait pas initialiser des variables "hors fonction" (statiques ou variables d'instance) dans une méthode.

    Je ne vois pas pourquoi. Je n'ai jamais dit que les variables dans une méthode sont forcément en autorelease. de plus je parle de release/retain sur les arguments de la méthode, pas sur les variables créées/initialisées dans la méthode elle-même : Si dans une méthode on pose un retain sur une variable d'instance, elle n'est pas marquée dans le pool puisque on a utilisé retain et non autorelease.

    J'ai l'impression que vous imaginez l'existence d'un mode spécial pour la gestion des variables qui ont été déclarés en autorelease. Cela n'existe pas. Il y a seulement le système du retain count en Obj-C. autorelease se rapport au système retain/release, en décalant simplement le release à  une date ultérieure.  
  • Philippe49Philippe49 Membre
    13:23 modifié #15
    dans 1220207723:

    dans 1220205412:

    J'ai pas vérifié, mais on peut très bien imaginer un mécanisme de ce type :

    Non, on ne peut pas imaginer ça. Sinon on ne pourrait pas initialiser des variables "hors fonction" (statiques ou variables d'instance) [edit]avec des objets autoreleasés[/edit] dans une méthode !

    [edit]J'avais oublié de finir ma phrase :)


    Ben sans problème, l'autorelease ne prend effet au plus tôt qu'à  la fin de la méthode, quand on initialise par un objet en autorelease, on fait un retain donc le compte (retainCount) y est.
  • ChachaChacha Membre
    13:23 modifié #16
    On ne s'est pas compris :

    @interface Toto : NSObject
    NSString* titi;
    @end

    @implemetation Toto

    -(void) f
    {
      [self initTiti];
      [self displayTiti];
      titi = nil;
    }

    -(void) initTiti
    {
      self->titi = [NSString stringWithString:@titi] //autoreleasé
    }

    -(void) displayTiti
    {
      NSLog(@titi = %@", titi); //d'après ton mécanisme imaginaire, ce serait interdit ici
    }

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