[Résolu] Gestion de mémoire dealloc-release

kaseykasey Membre
juillet 2008 modifié dans API AppKit #1
Bonjour,

Alor voila je vous explique mon problème : j'ai réaliser une petite classe toute bête dans le but de télécharger des fichiers a la volée en appelant simplement ma classe. Seulement il se trouve que je gère mal la mémoire et que soit l'application crash soit j'ai une fuite de mémoire.
je pense qu'il sajit ici d'un cas typique de débutant, mais j'ai beau plonger et replonger dans Cocoa par la pratique, j'ai un peu de mal a assimiler les mecanismes de mémoire.

Pouriez-vous jetter un coup d'oeil a ma classe et m'aider un peu lié tout ça dans ma tête?

<br />//<br />//&nbsp; CDLSynchrone.m<br />//&nbsp; TestDownloadSynchro<br />//<br />//&nbsp; Created by Kasey on 30/07/08.<br />//&nbsp; Copyright 2008 __MyCompanyName__. All rights reserved.<br />//<br /><br />#import &quot;CDLSynchrone.h&quot;<br /><br />@implementation CDLSynchrone<br />-(void) setUrl:(NSString *)url {<br />	m_url = [[NSString alloc] initWithString:url];<br />}<br /><br />-(void)	setDest:(NSString *)path {<br />	m_path = [[NSString alloc] initWithString:path];<br />}<br /><br />-(void) startDownload {<br />	<br />&nbsp; &nbsp; // création de l&#39;url qui pointe vers la ressource à  télécharger<br />&nbsp; &nbsp; m_NSurl = [NSURL URLWithString:m_url];<br />	<br />&nbsp; &nbsp; // téléchargement de la ressource pointée par l&#39;url (le résultat est mis en NSData)<br />&nbsp; &nbsp; m_data = [m_NSurl resourceDataUsingCache:NO];<br />	<br />&nbsp; &nbsp; // écriture du NSData dans un fichier sur le disque dur à  la racine<br />&nbsp; &nbsp; [m_data writeToFile:m_path atomically:NO];<br /><br />}<br /><br />-(void) dealloc {<br />	[m_url release];<br />	[m_path release];<br />	[m_NSurl release];	//&lt;--- paf patatras<br />	[m_data release];	//&lt;--- paf patatras<br />	<br />	[super dealloc];<br />}<br />@end<br />


Merci d'avance  :)
Et merci a Bru pour son exemple

Réponses

  • NoNo Membre
    17:05 modifié #2
    Si tu ne créés pas un objet par init..., ou si tu ne fais pas de retain dessus, alors, interdit de faire un release, sinon PATATRAS.

    C'est le cas de m_NSUrl et m_data (pas de init, ni de retain). Ce sont des objets alloués en mémoire de manière temporaire. Ils sont donc automatiquement détruits (enfin plutôt releasés) par le système (à  la prochaine boucle d'événement).

    Dans la doc Apple, vas faire un tour vers la gestion de la mémoire, chapitre NSAutoReleasePool.

    Sinon, il y a aussi une fuite mémoire potentielle dans les 2 méthodes set.... Par exemple, si tu fais plusieurs setUrl, les précédentes NSStrings seront perdues et donc jamais détruites.
  • AliGatorAliGator Membre, Modérateur
    juillet 2008 modifié #3
    1) Pour les setters :
    http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAccessorMethods.html

    Il ne faut pas faire une simple affectation, mais faire le retain du nouveau, puis le release de l'ancien avant d'affecter.

    2) Pour les crashes, tout est résumé ici dans la doc Apple
    This is the fundamental rule:

        * You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.

    Donc il ne fait faire des release que si c'est toi qui a alloué l'objet avec alloc, new ou copy. Si c'est un autre objet qui a alloué ta variable, c'est à  lui de se démerder pour la relâcher, et si c'est des variablse créées avec les constructeurs de commodité, ils sont automatiquement placés dans l'autoreleasepool donc pas de release à  envoyer non plus.

    Bon grillé par No mais moi j'ai mis les URLs :P
  • AliGatorAliGator Membre, Modérateur
    17:05 modifié #4
    Autre remarque :
    m_NSurl et m_data semblent être des variables d'instance de ta classe (vu leur nommage et le fait que tu les déclares pas localement).
    Pourtant, tu les construit avec des constructeurs de commodité, qui renvoient des objets autoreleasés.

    Conclusion, dès la prochaine runloop, ils n'existeront probablement plus en mémoire, puisqu'ils auront été automatiquement releasés !

    Donc pour moi il faut surtout que m_NSurl et m_data soit en fait juste des variables locales à  ta méthode [tt]startDownload:[/tt] (car elles n'ont pas besoin d'être des variables d'instance de ta classe à  priori en plus). Du coup le fait qu'elles soient autorelease tombe très bien, ça correspond au cas d'utilisation des variables temporaires / locales... et du coup plus besoin d'appeler release dans ton dealloc sur ces variables (ce que tu ne pourrais de toute façon pas faire puisque dans ce scope là  elles ne sont pas connues).
  • kaseykasey Membre
    17:05 modifié #5
    Haaaaaaa okkk tout s'éclaire  :kicking:

    Une question tout de même j'ai déjà  croisé des new aussi pour crée de nouvelles instances (enfin si l'on peut dire cela ^^)
    un new équivaut-il a un alloc ?
  • AliGatorAliGator Membre, Modérateur
    17:05 modifié #6
    1) new = alloc + init
    2) constructeur de commodité = alloc + init + autorelease
    exemple : [tt][NSString string] = [[[NSString alloc] init] autorelease] = [[NSString new] autorelease][/tt]
    exemple 2 : [tt][NSString stringWithString:@toto] = [[[NSString alloc] initWithString:@toto] autorelease][/tt]
  • kaseykasey Membre
    juillet 2008 modifié #7
    Ha ok merci bien pour les infos c'est vraiment très intéressant.

    par contre j'ai recodé ma classe et adapté mon appli mais j'ai toujours une fuite de mémoire...

    je sais pas regardez voir si vous comprenez d'où celle-ci peut bien provenir Oo :

    //<br />//&nbsp; CDLTest.m<br />//&nbsp; TestDownloadSynchro<br />//<br />//&nbsp; Created by Kasey on 30/07/08.<br />//&nbsp; Copyright 2008 __MyCompanyName__. All rights reserved.<br />//<br /><br />#import &quot;CDLTest.h&quot;<br /><br /><br />@implementation CDLTest<br /><br />- (IBAction)GoDL:(id)sender {<br />	CDLSynchrone *newDL ;<br />	if(!newDL){[newDL dealloc];}<br />	newDL = [CDLSynchrone alloc] ;<br />	[newDL startDownloadWithUrl:@&quot;http://127.0.0.1/&quot; andPath:@&quot;/Users/kasey/Desktop/fichier.html&quot;];<br />	<br />}<br />@end<br />
    


    //<br />//&nbsp; CDLSynchrone.m<br />//&nbsp; TestDownloadSynchro<br />//<br />//&nbsp; Created by Kasey on 30/07/08.<br />//&nbsp; Copyright 2008 __MyCompanyName__. All rights reserved.<br />//<br /><br />#import &quot;CDLSynchrone.h&quot;<br /><br /><br />@implementation CDLSynchrone<br /><br />-(void)startDownloadWithUrl: (NSString *)url andPath:(NSString *)path<br />{<br />&nbsp; &nbsp; NSURL	 *m_url ;<br />&nbsp; &nbsp; NSData	 *m_data ;<br />	<br />&nbsp; &nbsp; // création de l&#39;url qui pointe vers la ressource à  télécharger<br />&nbsp; &nbsp; m_url = [NSURL URLWithString:url];<br />	<br />&nbsp; &nbsp; // téléchargement de la ressource pointée par l&#39;url (le résultat est mis en NSData)<br />&nbsp; &nbsp; m_data = [m_url resourceDataUsingCache:NO];<br />	<br />&nbsp; &nbsp; // écriture du NSData dans un fichier sur le disque dur<br />&nbsp; &nbsp; [m_data writeToFile:path atomically:NO];<br />}<br /><br />@end<br />
    


    Je ne vois vraiment pas...
    Si vous trouvez que j'abuse de votre patience n'hésitez pas a me le dire :)
  • fouffouf Membre
    17:05 modifié #8
    Il y a un pb dans ton code lorsque tu initialises newDl.
    Si tu fais newDl = [CDLSynchrone alloc]; ca ne vas pas aller : tu as bien créé l'objet newDl mais tu ne l'as pas initialisé (ce que tu dois faire avec une méthode du genre init ou initWithBidule: ...). Cette étape est primordiale lorsque tu crées un objet en Cocoa (en effet, l'implémentation de init par NSObject ne fais pas rien ;) )

    Ensuite, je pense que tu t'es planté lorsque tu écris if(!newDL){[newDL dealloc];}. En effet, il faudrait plutot if(newDL){[newDL dealloc];}
  • kaseykasey Membre
    17:05 modifié #9
    Ha exact merci de me le faire remarquer, en effet même si je suis le créateur de la classe celle-ci hérite deja de NSObject :)

    Par contre pour [tt]if(!newDL) {[newDL dealloc];}[/tt] si je l'écris [tt]if(newDL) {[newDL dealloc];}[/tt] (ce qui me semble plus logique a moi aussi) l'application essaye de dealloc au premier passage Oo
  • AliGatorAliGator Membre, Modérateur
    juillet 2008 modifié #10
    Heu c'est quoi ce [tt]if (newDL) { [newDL dealloc]; }[/tt] ?? c'est du grand n'importe quoi aussi ça :P

    1) Ne jamais appeler dealloc directement ! On n'appelle que release (ou autorelease) qui appellera tout seul dealloc si besoin (si le retainCount de l'objet passe à  zéro)

    2) De toute façon tu viens de déclarer newDL ([tt]CDLSynchrone* newDL[/tt]) donc il n'a de toute façon été alloué nulle part avant, c'est une variable locale ! (par contre c'est pas dit qu'elle soit nil, et c'est pour ça que ça appelle dealloc au premier passage : c'est un pointeur non initialisé donc il peut valoir n'importe quoi et pointer sur n'importe quoi puisque pas encore initialisé justement)
  • kaseykasey Membre
    17:05 modifié #11
    oui oui AliGator !! Aà¯e me frappe pas  B)
    En effet l'idée été de désalloué l'instance si une nouvelle devais être crée mais tout simplement ceci passe mieux peut être :

    - (IBAction)GoDL:(id)sender {	<br />	CDLSynchrone *newDL ;<br />	<br />	newDL = [CDLSynchrone new] ;<br />	[newDL startDownloadWithUrl:@&quot;http://127.0.0.1/&quot; andPath:@&quot;/Users/kasey/Desktop/fichier.html&quot;];<br />	[newDL release];<br />}
    


    Hop on release comme ça pu de problème  :kicking:

    J'espère  :-\\
  • laurrislaurris Membre
    17:05 modifié #12
    J'veux pas dire (mais j'le dis quand même) que resourceDataUsingCache: est déprécié depuis 10.4. Si on a Safari installé, il vaut mieux utiliser  NSURLConnection.
  • AliGatorAliGator Membre, Modérateur
    17:05 modifié #13
    dans 1217442170:

    - (IBAction)GoDL:(id)sender {	<br />	CDLSynchrone *newDL ;<br />	<br />	newDL = [CDLSynchrone new] ;<br />	[newDL startDownloadWithUrl:@&quot;http://127.0.0.1/&quot; andPath:@&quot;/Users/kasey/Desktop/fichier.html&quot;];<br />	[newDL release];<br />}
    


    Hop on release comme ça pu de problème  :kicking:

    J'espère  :-\\
    Ah ben voilà , c'est mieux déjà  :)
  • kaseykasey Membre
    juillet 2008 modifié #14
    @AliGator

    Bon ben merci pour tout j'habite dans le sud du coté de Tarbes je te paye un coup pour te remercier quant tu veux  :p
    Et l'invitation est valable pour tous les membres de objective-cocoa ^^ mais pas tous du coup sinon mon portefeuille vas pleurer ^^

    @laurris

    Oui en effet elle est totalement déprécié et tellement bugged que j'ai recodé ma classe comme ceci :

    //<br />//&nbsp; CDLSynchrone.m<br />//&nbsp; TestDownloadSynchro<br />//<br />//&nbsp; Created by Kasey on 30/07/08.<br />//&nbsp; Copyright 2008 __MyCompanyName__. All rights reserved.<br />//<br /><br />#import &quot;CDLSynchrone.h&quot;<br /><br /><br />@implementation CDLSynchrone<br /><br />-(int)startDownloadWithUrl: (NSString *)url andPath:(NSString *)path<br />{<br />	NSURL		*m_url;<br />	NSError		*m_error;<br />&nbsp; &nbsp;&nbsp; &nbsp;  NSData		*m_data;<br />	NSURLResponse	*m_response;<br />		<br />	m_url = [ NSURL URLWithString:url];<br />	<br />	NSURLRequest *m_request = [ NSURLRequest requestWithURL:m_url cachePolicy:NSURLRequestReloadIgnoringLocalAndRemoteCacheData timeoutInterval:60.0];<br />	<br />	m_data = [ NSURLConnection sendSynchronousRequest:m_request returningResponse:&amp;m_response error:&amp;m_error]; <br />	[m_data writeToFile:path atomically:NO];<br />	<br />	return [m_error code];<br />}<br /><br /><br />@end
    

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