Collection de références sans retain/release
Flo
Membre
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.
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.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
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 ?
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).
Les collections font toutes cela.
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...
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
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.
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 ?
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.
B connaà®t A donc C, et tient à jour une collection d'indices, soit un tableau NSInteger indices[]; en dur, soit un NSIndexSet
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 ?
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 ?
Le but, c'est que si tu supprimes B, ça ne désalloue pas C, non ?
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
cela me semble la solution dans l'esprit Obj-C
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...
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 ?
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 ...
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")
C'est pas bête...
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 !
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)
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 !
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);
}
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.
Je vais tester tout ça et je verrai bien en fonction du reste...