Collection de références sans retain/release

FloFlo Membre
08:45 modifié dans API AppKit #1
Bonjour à  tous,

Ma question semble sûrement bête mais, ya-t-il des collections qui ne "possèdent" pas les objets qu'elles références ?

En gros je cherche l'équivalent d'un NSMutableArray qui ne fasse pas de retain implicite quand on lui ajoute un objet et qui ne fasse pas de release implicite quand on détruit la collection.

Merci d'avance pour vos réponses.

Flo.

Réponses

  • mpergandmpergand Membre
    08:45 modifié #2
    Un tableau en C .
    Un vector en C++ stockant des pointeurs.
    ;)

    Néanmoins ta question est étrange, car le retain/release effectué par NSMutableArray est neutre si ces objets sont retenus par ailleurs.

    Quel est ton problème en fait ?
  • Philippe49Philippe49 Membre
    janvier 2009 modifié #3
    Il y a surement une autre façon de procéder que de changer le comportement des NSArray !


    Il faudrait sous-classer et redéfinir beaucoup de méthodes addObject,removeObject, ... Travail bien lourd, et d'autant plus diffciile que cette classe est plutôt opaque et en même temps particulièrement optimisée.
    Peut-être que sur-classer serait plus facile, en prévoyant les méthodes qui sont nécessaires.

    Doc sur NSArray : Subclassing Notes
    Most developers would not have any reason to subclass NSArray. The class does well what it is designed to do"maintain an ordered collection of objects. But there are situations where a custom NSArray object might come in handy. Here are a few possibilities:

    Changing how NSArray stores the elements of its collection. You might do this for performance reasons or for better compatibility with legacy code.
    Changing how NSArray retains and releases its elements.
    Acquiring more information about what is happening to the collection (for example, statistics gathering).


    dans 1231440949:

    En gros je cherche l'équivalent d'un NSMutableArray qui ne fasse pas de retain implicite quand on lui ajoute un objet et qui ne fasse pas de release implicite quand on détruit la collection.

    Les collections font toutes cela.
  • AliGatorAliGator Membre, Modérateur
    08:45 modifié #4
    Je vois vraiment pas non plus l'intérêt de faire ça, mais bon, si tu as vraiment besoin, une autre idée comme ça en passant (je sais pas si elle est bonne mais bon :P)

    L'idée serait de créer une classe Wrapper contenant une [tt]@property(assign) id object;[/tt]. Et ensuite au lieu d'ajouter l'objet A à  ton tableau, tu ajoutes un Wrapper qui lui-même contient l'objet A. Comme ça l'objet Wrapper lui-même recevra les retain/release, mais l'objet que ce dernier contient lui ne recevra pas de retain/release.

    Après, il suffit de créer une classe genre NonRetainingArray qui encapsule tout ça, soit une sous-classe de NSArray qui redéfinit les addObject, removeObject, etc... soit une sur-classe (classe qui ne dérive de rien mais contient un NSArray, et définit les méthodes addObject/removeObject/..., qui se charge d'encapsuler/désencapsuler l'objet avant de l'ajouter/supprimer au NSArray interne...


    Ainsi, avec le Wrapper, tu évites ainsi que l'objet contenu dans le Wrapper reçoive le retain. Maintenant, ça ne change pas trop le problème à  savoir que si justement l'objet, auquel tu n'envoies pas le retain, est détruit, ben ton Wrapper pointera sur du n'importe quoi...
  • ChachaChacha Membre
    janvier 2009 modifié #5
    dans 1231440949:

    Ma question semble sûrement bête mais, ya-t-il des collections qui ne "possèdent" pas les objets qu'elles références ?
    En gros je cherche l'équivalent d'un NSMutableArray qui ne fasse pas de retain implicite quand on lui ajoute un objet et qui ne fasse pas de release implicite quand on détruit la collection.

    Avec Garbage collector activé : NSHashTable.
    [edit]
    Je détaille un peu : NSHashTable utilise des références __weak et pas __strong. Donc un objet contenu dans une NSHashTable n'est pas "retenu" et peut être garbage collecté quand dans le reste du programme il n'est plus utilisé. Ce qui est magique, c'est qu'un lien __weak passe alors automatiquement à  nil (sinon ce serait très problématique).
    +
    Chacha
  • FloFlo Membre
    08:45 modifié #6

    Je vois vraiment pas non plus l'intérêt de faire ça


    En fait ce que je souhaite faire c'est, d'une part, un objet A possédant (via un NSMutableArray) une collection d'objet C. D'autre part plusieurs objets B qui peuvent référencer les C via une collection qui ne les "possède pas" (pas de retain/release).
    Le but étant, quand on supprime un objet C via A il est détruit car les B n'ont pas Fait de retain dessus.


    Je détaille un peu : NSHashTable utilise des références __weak et pas __strong. Donc un objet contenu dans une NSHashTable n'est pas "retenu" et peut être garbage collecté quand dans le reste du programme il n'est plus utilisé. Ce qui est magique, c'est qu'un lien __weak passe alors automatiquement à  nil (sinon ce serait très problématique).


    C'est exactement ça que je souhaite faire, sauf que je n'utilise pas le garbage collector.

    Sinon j'avais pensé à  une autre idée, utiliser partout des NSMutableArray et envoyer des notifications aux objets B chaque fois que le A détruit un C pour qu'à  leur tour les B fassent un release dessus. Vous en pensez quoi ?
  • ChachaChacha Membre
    08:45 modifié #7
    dans 1231491369:

    En fait ce que je souhaite faire c'est, d'une part, un objet A possédant (via un NSMutableArray) une collection d'objet C. D'autre part plusieurs objets B qui peuvent référencer les C via une collection qui ne les "possède pas" (pas de retain/release).
    Le but étant, quand on supprime un objet C via A il est détruit car les B n'ont pas Fait de retain dessus.


    Mais dans ce cas, tu te retrouves avec le problème que "B" contient des objets devenus invalides. Comment les supprimer ? Ben je n'en sais rien... À ma connaissance, il n'y a pas de mécanisme automatique mettant un lien à  nil hors de l'environnement GC.

    Ton système de notifications marcherait, mais ce serait lourd (ça risque d'engorger le notification center, qui doit à  chaque fois tester à  qui envoyer telle notification, etc.)

    Une autre idée (pas pratique non plus) : si tes objets "C" sont toujours d'une classe que tu as développée toi-même, tu peux faire en sorte que le init et le dealloc se chargent d'ajouter/enlever l'objet courant d'une collection "B" connue.
  • Philippe49Philippe49 Membre
    janvier 2009 modifié #8
    Une solution
    B connaà®t A donc C, et tient à  jour une collection d'indices, soit un tableau NSInteger indices[]; en dur, soit un NSIndexSet
  • FloFlo Membre
    08:45 modifié #9

    Une autre idée (pas pratique non plus) : si tes objets "C" sont toujours d'une classe que tu as développée toi-même, tu peux faire en sorte que le init et le dealloc se chargent d'ajouter/enlever l'objet courant d'une collection "B" connue.


    En gros il faudrait que chaque C possède une référence sur les B qui le contienne. Le problème c'est que pour faire ça je doit utiliser une collection qui va "retenir" les objets B... Donc si on supprime un B ça désalloue C qui désalloue B... ça va pas poser problème ?


    B connaà®t A donc C, et tient à  jour une collection d'indices, soit un tableau NSInteger indices[]; en dur, soit un NSIndexSet


    Oui mais le problème c'est que si A fait un insert d'un nouveau C et ben les indices que connaissent les B deviennent faut puisqu'ils sont décalés... Il faudrait donc mettre à  jour les indices de tout les B qui connaissent les anciens C lorsqu'on fait un insert ou un delete... C'est pas un peu lourd ?
  • ChachaChacha Membre
    08:45 modifié #10
    dans 1231498092:


    Une autre idée (pas pratique non plus) : si tes objets "C" sont toujours d'une classe que tu as développée toi-même, tu peux faire en sorte que le init et le dealloc se chargent d'ajouter/enlever l'objet courant d'une collection "B" connue.


    En gros il faudrait que chaque C possède une référence sur les B qui le contienne. Le problème c'est que pour faire ça je doit utiliser une collection qui va "retenir" les objets B... Donc si on supprime un B ça désalloue C qui désalloue B... ça va pas poser problème ?


    Le but, c'est que si tu supprimes B, ça ne désalloue pas C, non ?
  • Philippe49Philippe49 Membre
    janvier 2009 modifié #11
    Pourquoi B n'aurait-il pas tout simplement une NMutableArray qui stockent des références sur les objets qu'on trouvent également dans C. Et alors la solution c'est justement que les objets soient retained/released et non  le contraire.
    Pourquoi veux-tu passer par la NSMutableArray de A ? surement pas par économie de mémoire, tout cela se fait par adresses !


    et suite au prochain post
  • Philippe49Philippe49 Membre
    08:45 modifié #12

    dans 1231491369:

    ... envoyer des notifications aux objets B chaque fois que le A détruit un C pour qu'à  leur tour les B fassent un release dessus. Vous en pensez quoi ?

    cela me semble la solution dans l'esprit Obj-C
  • FloFlo Membre
    08:45 modifié #13

    Le but, c'est que si tu supprimes B, ça ne désalloue pas C, non ?


    Oui c'est ça, mais dans ta solution ce sont les C qui connaissent les B qui les référencent or moi j'ai besoin que les B connaissent les C qu'ils contiennent sans les retenir...


    Pourquoi veux-tu passer par la NSMutableArray de A ?


    Dans mon cas A doit connaà®tre TOUT les C, et chaque B doit connaà®tre les C qu'il gère.



  • Philippe49Philippe49 Membre
    08:45 modifié #14
    dans 1231587704:

    Dans mon cas A doit connaà®tre TOUT les C, et chaque B doit connaà®tre les C qu'il gère.

    Et la version arborescente : A connaà®t tous les B , et connaà®t tous les C au deuxième niveau ?
  • FloFlo Membre
    08:45 modifié #15

    Et la version arborescente : A connaà®t tous les B , et connaà®t tous les C au deuxième niveau ?


    J'y ai pensé, le problème c'est qu'il existe des C qui ne sont connus par aucun B mais juste par A.
  • Philippe49Philippe49 Membre
    08:45 modifié #16
    dans 1231588743:

    J'y ai pensé, le problème c'est qu'il existe des C qui ne sont connus par aucun B mais juste par A.

    Tu fais un B virtuel ...
  • AliGatorAliGator Membre, Modérateur
    08:45 modifié #17
    C'est ce que j'allais proposer aussi (avant de voir qu'il y avait une 2e page dans ce sujet :P)...
    L'avantage du "B virtuel" c'est que ça te permet en plus en un clin d'oeil de savoir les C "non classés", connus d'aucun B quoi.
    Par exemple si les C ce sont des photos et les B des albums (et A une photothèque), tu peux facilement afficher les photos encore classées dans aucun album, ce qui permet justement à  l'utilisateur de voir les non-classées/non-triées et de les trier en conséquence  ;) (Ce qui du coup quand il va les faire passer dans un album va juste consister à  les rajouter au B concerné et les enlever du "B virtuel")
  • FloFlo Membre
    08:45 modifié #18

    Tu fais un B virtuel ...


    C'est pas bête...


    ce qui permet justement à  l'utilisateur de voir les non-classées/non-triées et de les trier en conséquence  (Ce qui du coup quand il va les faire passer dans un album va juste consister à  les rajouter au B concerné et les enlever du "B virtuel")


    Oui en effet, la question que je me pose c'est comment faire quand plusieurs albums peuvent contenir la même photo, sans faire de copie ?

    En tous cas merci à  tous pour votre aide !

  • AliGatorAliGator Membre, Modérateur
    janvier 2009 modifié #19
    Si une "photo" (C) peut être dans plusieurs "albums" (B), alors je dirais que ce n'est plus tout à  fait la même vision des choses : il faut une "photothèque" (A) qui contienne toutes les photos C d'un côté, et en plus les photos C peuvent aussi se trouver dans un ou plusieurs albums B.

    Dans ce cas dans ta photothèque A tu as un NSArray contenant toutes tes photos C, de toute façon. Et en plus, tu as aussi une collection d'albums B, qui peut contenir des photos C (photos qui seront de toute façon dans ta photothèque A). Ainsi tes photos C sont retenues de toute façon par A, + par autant de B qui les contiennent (retainCount d'un C = 1 + nombre de B qui contiennent C)

    Maintenant c'est vrai que si tu veux savoir alors facilement dans quels albums B se trouve une photo C donnée, soit tu parcoures alors tous tes albums B et leur demande s'ils contiennent la photo C, ce qui en soi n'est pas si idiot, soit quand tu rajoutes une photo C à  un album B, tu mémorises dans C l'album B, c'est à  dire que tu rajoutes B à  une liste interne à  C d'albums auxquels C appartient... mais là  on retrouve ton souci d'avoir besoin d'un tableau/conteneur de "weak references"... (Problème exposé ici chez Apple mais s'ils donnent une solution pour une weak reference à  un seul objet, aucune pour un tableau de weak references...).

    Donc si tu as besoin de cette fonctionnalité (savoir dans quels albums B se trouve une photo C donnée), un parcours de tous tes albums B pour leur demander s'ils contiennent la photo C sera peut-être plus simple à  mettre en oeuvre (et moins risqué à  gérer car sinon il ne faut pas que tu oublies d'ajouter/supprimer les weak references dans tous les cas utiles)
  • FloFlo Membre
    08:45 modifié #20

    mais là  on retrouve ton souci d'avoir besoin d'un tableau/conteneur de "weak references"...


    C'est exactement pour ça que je pose la question ! Je voulais savoir s'il existait une solution pas trop lourde m'évitant de parcourir tous les B quand on supprime un C de A.
    Mais bon visiblement, comme tu dis, c'est peut-etre plus judicieux de faire un parcours d'autant plus qu'il n'y a pas beaucoup de niveaux à  visiter...

    Merci !
  • Philippe49Philippe49 Membre
    janvier 2009 modifié #21
    Soyons fou : qu'est-ce-qui nous empêche vraiment d'utiliser  void * ou CFTypeRef ?
    Je sais que ce n'est pas  "très obj-C correct", et alors ? que fait CoreFoundation ?
    Ainsi le fils C peut tenir à  jour un tableau CFTypeRef albums[]; de ses (multiples) parents

    Ne pas oublier également qu'existe NSMutableSet avec les méthodes setWithArray: addObjectsFromArray: ...

    #import <Foundation/Foundation.h>

    int main( int argc, char **argv )
    {
    NSAutoreleasePool * popaul=[[NSAutoreleasePool alloc] init];
    NSSet * aSet=[NSSet setWithObjects:@Hello World,@Coucou Tout le Monde,nil];
    void * aSetRef=(void*) aSet;   // CFTypeRef aSetRef=(CFTypeRef)aSet;
    NSLog(@%@\;nretain count = %lu",aSet,[aSet retainCount]);

    NSSet * mySet=(NSSet *) aSetRef;
    NSLog(@%@\;nretain count = %lu",mySet,[mySet retainCount]);
    [popaul drain];
    return (0);
    }


  • AliGatorAliGator Membre, Modérateur
    08:45 modifié #22
    En allant faire un tour sur la page Collections Programming Topics for Cocoa j'ai vu dans les "Related references" à  gauche une classe que je ne connaissais pas : NSPointerArray.
    Ca vaut le coup de creuser dans cette classe, entre autres avec les [tt]NSPointerFunctionsOptions[/tt] comme [tt]NSPointerFunctionsZeroingWeakMemory[/tt] (et non pas laisser la valeur par défaut [tt] NSPointerFunctionsStrongMemory[/tt]) je pense que tu peux avoir une collection qui n'envoie pas de retain/release.
    Remarque, c'est pareil pour NSHashTable, qui n'utilise par défaut que des weak references... mais semble à  l'aide des options pouvoir gérer aussi une sorte de weak reference même en environnement non-GC (faut juste pas laisser les options par défaut)

    Maintenant, ces classes sont 10.5 only (et ont manifestement été créées avec l'avènement du Garbage Collector mais semblent pouvoir fonctionner sans, et du coup éviter quand même les retain/release si tu lui mets les bonnes options).






    Sinon si ces classes ne conviennent pas même avec les options (je n'ai pas testé), ou si tu veux faire un truc qui ne nécessite pas 10.5 minimum, tu peux peut-être créer ta propre classe conteneur...

    Philippe49 propose les tableaux C, moi je suis pas fan car il faut gérer leur taille, et leur réallocation lorsque tu n'as plus de place dans le tableau pour en créer un plus grand... par contre tu peux utiliser les listes chaà®nées, ce qui a le mérite de ne pas être trop dur à  créer/gérer (pas de réallocation d'une taille d'un tableau, facilité d'insertion en plein milieu de la liste si besoin, ...) et devrait convenir à  tes besoins.

    Une autre solution pourrait consister à  associer à  tes albums une clé unique (un ID, ou un hashCode). Et du coup de stocker dans tes photos C non pas un tableau d'albums B qui contiennent cette photo, mais un tableau d'ID de ces albums. Ainsi, tu as beau rajouter l'ID d'un album dans ce tableau d'albums dans C, l'album lui-même ne reçoit pas de "retain", mais ça ne t'empêche pas de retrouver facilement tous les albums B qui contiennent une photo C donnée, à  partir de leur ID/hashCode.
  • FloFlo Membre
    08:45 modifié #23
    Merci pour ces solutions !

    Je vais tester tout ça et je verrai bien en fonction du reste...
Connectez-vous ou Inscrivez-vous pour répondre.