[Gestion mémoire] Image récupérée d'une url
6ix
Membre
Bonjour,
J'ai beau avoir lu la doc et de nombreux messages au sujet de la gestion mémoire, je reste quelque peu dans le flou concernant une utilisation un peu plus poussée.
J'ai plusieurs classes qui contiennent notamment une image (UIImage, c'est sur iPhone, mais cela ne change rien), que je récupère depuis un url. J'ai donc fait une classe "utilitaire", définie comme singleton, comportant une méthode allant chercher cette image et la retournant. Pour initialiser mes images, j'appelle donc cette méthode depuis mes classes. Mon problème est que je ne suis pas certain de vider efficacement la mémoire; le but serait que lorsqu'une instance d'une de ces classes soit désallouée, l'image le soit aussi.
Le code ma méthode récupérant une image:
Et celui de l'initialisation de mes classes:
Est-ce vraiment une bonne façon de faire?
Si j'ai compris juste, la 1e méthode renvoie une image en autorelease; lors de l'initialisation, cette image reçoit un retain (image est une property définie comme (nonatomic, retain)); lorsque mon instance appelle dealloc, l'image reçoit un release, et comme l'objet n'est plus utilisé par personne, il devrait être définitivement désalloué par l'autorelease pool "un peu après"...?
J'ai beau avoir lu la doc et de nombreux messages au sujet de la gestion mémoire, je reste quelque peu dans le flou concernant une utilisation un peu plus poussée.
J'ai plusieurs classes qui contiennent notamment une image (UIImage, c'est sur iPhone, mais cela ne change rien), que je récupère depuis un url. J'ai donc fait une classe "utilitaire", définie comme singleton, comportant une méthode allant chercher cette image et la retournant. Pour initialiser mes images, j'appelle donc cette méthode depuis mes classes. Mon problème est que je ne suis pas certain de vider efficacement la mémoire; le but serait que lorsqu'une instance d'une de ces classes soit désallouée, l'image le soit aussi.
Le code ma méthode récupérant une image:
<br />- (UIImage *)getImageWithName:(NSString *)aName {<br /> NSURL *url = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"%@?media=%@", kImagesURL, aName]];<br /> NSData *dataImage = [[NSData alloc] initWithContentsOfURL:url];<br /> UIImage *image = [UIImage imageWithData: dataImage];<br /> [url release];<br /> [dataImage release];<br /> <br /> return image;<br />}<br />
Et celui de l'initialisation de mes classes:
<br />- (id)initWithDictionary:(NSDictionary *)aDictionary {<br /> if(self = [super init]) {<br /> self.name = [aDictionary valueForKey:EXP_NAME];<br /> self.description = [aDictionary valueForKey:EXP_DESCRIPTION];<br /> //...<br /><br /> self.image = [[NetworkUtility sharedInstance] getImageWithName:[aDictionary valueForKey:EXP_IMAGE]];<br /> }<br /> <br /> return self;<br />}<br /><br />- (void)dealloc {<br /> [name release];<br /> [description release];<br /> [image release];<br /> <br /> [super dealloc];<br />}<br />
Est-ce vraiment une bonne façon de faire?
Si j'ai compris juste, la 1e méthode renvoie une image en autorelease; lors de l'initialisation, cette image reçoit un retain (image est une property définie comme (nonatomic, retain)); lorsque mon instance appelle dealloc, l'image reçoit un release, et comme l'objet n'est plus utilisé par personne, il devrait être définitivement désalloué par l'autorelease pool "un peu après"...?
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Quand tu envoies un -[autorelease], l'objet est ajouté au pool.
Le runtime ObjC exécute en permanence une boucle (boucle d'évenement). Quand on arrive à la fin de la boucle, le pool envoie un -[release] à tous les objets qu'il contient.
En partant du principe que l'instance de ta classe n'est pas créée et libérée dans le même cycle du runtime: quand sera exécutée la méthode -[dealloc] de ta classe, l'image aura déjà reçu le -[release] de la part du pool.
Donc, ton image sera libérée dans la méthode -[dealloc], et pas plus tard.
En effet ta classe singleton et sa méthode getImageWithName retourne une image "autorelease" donc c'est parfait, au final elle ne garde rien pour elle-même (elle release ce qu'elle a alloué et dont elle n'a plus besoin, et elle autorelease ce qu'elle retourne, donc plus rien n'est directement "retained" par elle).
Et puisque ta @property image est en "nonatomic, retain" en effet ça veut dire que l'accesseur met les retain qu'il faut quand tu affectes ton image à self.image... et qu'il faut penser à envoyer un "release" à cette propriété "image" lors du dealloc... ce que tu fais aussi, donc nickel.
Pour info on peut aussi écrire dans le "dealloc" un [tt]self.image = nil[/tt], qui revient à peu près au même que faire un release (puisque affecter une nouvelle valeur à self.image envoie un release à l'ancienne et un retain à la nouvelle, mais là la nouvelle c'est nil, donc ça n'a pas d'incidence).
Certains préfèreront écrire [tt]self.image = nil[/tt] plutôt que [tt][image release][/tt] pour une simple question de "symétrie" : en effet on s'assure en général d'avoir équilibré nos appels mémoire en comptant le nombre de alloc/copy/retain et en vérifiant qu'il y a autant de release/autorelease, et pour les property on s'assure de les avoir remis à nil en sortant (dans le dealloc) pour se retrouver dans le même état à la destruction qu'à la création, comme ça on a une sorte de "symétrie" dans les appels.
Mais bon en réalité ça change rien du tout, si c'est plus clair pour toi d'envoyer un release à image dans le dealloc plutôt que de passer image à nil, vu que ta propriété est en mode "retain", ça change strictement rien.
L'avantage aussi de mettre à nil plutôt que d'envoyer un release c'est que si un jour, pour une raison quelconque, tu changeais les attributs de ta propriété pour mettre "assign" au lieu de "retain", alors le [tt]self.image=nil[/tt] n'aurait aucune incidence alors que le [tt][image release][/tt] n'aurait plus de raison d'être, et si tu oublies alors de l'enlever ça fera crasher ton appli...
Voilà , c'était la petite remarque en passant
En effet, si je comprends le principe, j'ai un peu de peine à comprendre encore exactement à quoi correspond une boucle d'événement; quand une boucle démarre et quand elle se termine.
J'avais compris l'idée de faire ainsi, par contre je ne voyais pas vraiment de raison pour le faire d'une façon ou d'une autre, tout est plus claire maintenant! J'en tiendrai compte pour la suite, c'est vrai que cela peut paraà®tre plus "logique".
Bon, évidemment, je schématise. Mais ce schéma explique au moins pourquoi tu ne peux pas lancer de calculs trop longs dans le thread principal: il va falloir un sacré bout de temps avant d'aller chercher les nouveau événements et les traiter.
Autre question pendant que j'y suis, toujours liée à la mémoire. Je me suis basé sur l'exemple d'Apple TheElements (exemple iPhone), à savoir une structure avec une classe singleton qui charge la liste de mes objets, en les mettant dans un dictionnaire de base et plusieurs autres collections pour divers tris. Ces collections sont ensuite consultées pour afficher les éléments dans une tableview.
Différence de taille, mes objets ne sont pas chargés une seule fois au lancement de l'application depuis un fichier plist, mais mis à jour plusieurs fois, depuis une url justement. Je voudrais donc être certain qu'avant chaque mise à jour de ces éléments, les "anciens" soient tous complètement supprimés.
Problème:
1) chaque élément est retenu par les collections qui le gèrent; il faudrait donc vider chacune de ces collections. Y a-t-il mieux à faire?
2) les cellules de la tableview utilisent ces éléments (avec un (nonatomic, retain) monElement dans la classe définissant une cellule); une cellule est créée en autorelease dans la méthode d'une tableview renvoyant la cellule adéquate, mais suis-je vraiment sûr que celle-ci sera supprimée avant ou peu après ma mise à zéro des données, détruisant ainsi monElement?