Dealloc non appelé

allianallian Membre
19:07 modifié dans API UIKit #1
Bonjour,
j'ai un soucis avec la méthode dealloc d'un de mes controlleurs qui n'est pas appelée.

Je m'explique. J'ai un controlleur principal qui gére tout un tas d'autres controlleurs qu'il affiche à  l'écran.

4 parmis mes controlleurs appelent bien la méthode dealloc. Mais le principal, celui qui gère mon jeu, une fois appelé removeFromSuperview n'appelent pas la méthode dealloc. Résultat l'occupation de la mémoire augmente au fur et a mesure des parties !!

Pourquoi la méthode dealloc n'est pas appelé ? Dois je realeser à  la main ou removeFromSuperview toutes les vues et autres controlleurs utilisés dans mon controlleur recalcitrant ou le tout devrait il se faire seul ??

Merci beaucoup encore une fois

Réponses

  • zoczoc Membre
    juin 2009 modifié #2
    La méthode n'est pas appelée car il manque un release quelque part  :)

    (C'est toujours le même problème).

    Maintenant, c'est difficile d'en dire plus, car d'une part je ne connais pas ton code, et d'autre part je suis au bureau (ou je travaille sur PC, pas sur Mac malheureusment) et je n'ai donc pas accès facilement à  la doc du SDK pour vérifier quelles properties font un retain et quelles properties ne le font pas.
  • allianallian Membre
    19:07 modifié #3
    Enfaite si j'ai bien compris d'apres ce que j'ai lu sur la doc d'apple, pour que le dealloc soit appelé il faut qu'avant de faire le removeFromSuperview j'ai releasé tout les objets que retien la classe.

    Tant que j'aurais pas fais cela la méthode ne sera pas appelé ?
  • AliGatorAliGator Membre, Modérateur
    19:07 modifié #4
    Heu si tu poses ce genre de question c'est que tu n'as toujours pas compris les mécanismes de retainCount utilisés en Cocoa pour tous les NSObjects... le balancement des retain/release et le mécanisme d'appel automatique à  dealloc selon ces conditions...

    Le dealloc n'est appellé que quand l'objet est supprimé de la mémoire. Ce qui n'est le cas que quand le retainCount tombe à  zéro.
  • allianallian Membre
    juin 2009 modifié #5
    Cela je l'ai bien compris c'est ce que j'essayé de dire, le dealloc est appelé uniquement quand le retain/release fait 0 pour l'objet.

    Ce que j'essayai de comprendre était si le même si le retainCount est à  0, est ce que l'objet se dealloc si par exemple un des objets que lui même retient n'a pas son retainCount à  0.
    Après logiquement il n'y en a pas d'après Clang, qui me dit que tout est ok, mais bon je parie qu'on ne peut pas se fier à  100%.

    EDIT : J'ai fais des nslog de mes retainCount et au début ça me dit 2 et après le release il me dit 8 !!
    C'est super bizarre surtout qu'apres le removeFromSuperview il me met bien 0 !Je comprends pas trop...

    RE EDIT : Je pense comprendre, le reainCount est à  8 car ma classe qui me dit ça, a elle, 8 propriété avec un retain. Ou je me trompe ?
  • CéroceCéroce Membre, Modérateur
    19:07 modifié #6
    dans 1244191399:

    Ce que j'essayai de comprendre était si le même si le retainCount est à  0, est ce que l'objet se dealloc si par exemple un des objets que lui même retient n'a pas son retainCount à  0.


    Oui, bien sûr qu'il se désalloue. Et donc, tu auras des fuites mémoire sur les objets qu'il n'a pas libérés (mais évidemment, cela ne devrait jamais arriver, un [release] devant être envoyé à  tous ces objets dans -dealloc).

    Encore une fois, tout cela est très bien expliqué dans le livre d'Aaron Hillegass.
  • allianallian Membre
    19:07 modifié #7
    Je désespère, je me suis revu tout mon code ligne par ligne en modifiant pas mal de trucs au niveau de la mémoire, Clang ne me trouve toujours pas d'erreurs mais j'ai plusieurs méthodes de dealloc qui ne s'appelent pas à  cause de retainCount qui sont genre à  2 ou 3 alors que je n'ai fait qu'une seule alloc...

    Genre :

    //initializing the sounds	<br />	NSString *path = [[NSBundle mainBundle] pathForResource:@&quot;Bonus&quot; ofType:@&quot;caf&quot;]; //indiquer le chemin de notre son en donnant le nom du fichier et son extension<br />	bonusSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]&nbsp; error:NULL]; //declarer un objet AVAudioPlayer<br />	bonusSound.delegate = self;<br />	[bonusSound performSelector:@selector(prepareToPlay) withObject:nil afterDelay:0.0];<br />	<br />	bonusSound2 = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]&nbsp; error:NULL]; //declarer un objet AVAudioPlayer<br />	bonusSound2.delegate = self;<br />	[bonusSound2 performSelector:@selector(prepareToPlay) withObject:nil afterDelay:0.0];<br />	<br />	path = [[NSBundle mainBundle] pathForResource:@&quot;Loose&quot; ofType:@&quot;caf&quot;]; //indiquer le chemin de notre son en donnant le nom du fichier et son extension<br />	deathSound = [[AVAudioPlayer alloc] initWithContentsOfURL:[NSURL fileURLWithPath:path]&nbsp; error:NULL]; //declarer un objet AVAudioPlayer<br />	deathSound.delegate = self;<br />	[deathSound performSelector:@selector(prepareToPlay) withObject:nil afterDelay:0.0];<br />	<br />	//basic init<br />	self.title = @&quot;Bleeker&quot;;<br />	self.view.userInteractionEnabled = YES;<br />	<br />	Labirynthe *myLabi = [[Labirynthe alloc] init];<br />	[myLabi copyArray:leLaby];<br />	CGRect frame = self.view.frame;<br />	myLabi.frame = frame;<br />	self.view = myLabi;<br />	self.view.backgroundColor = [UIColor clearColor];<br />	<br />	<br />	//init the counting<br />	decompte = 7;<br />	<br />	//init the bleeker<br />	self.monBleeker = nil;<br />	MyBleeker *tmpController = [[MyBleeker alloc] initWithNibName:@&quot;Bleeker&quot; bundle:nil];<br />	self.monBleeker = tmpController;<br />	[tmpController release];<br />	self.monBleeker.theLab = myLabi;<br />	[myLabi release];<br />	self.monBleeker.a = 2;<br />	self.monBleeker.b = 2;<br />	self.monBleeker.isBad = NO;<br />	self.monBleeker.directionBleeker = 1;<br />	self.monBleeker.counter = 1;<br /><br />	int rL, rH;<br />	//init each enemy and place it randomly<br />	tmpController = [[MyBleeker alloc] initWithNibName:@&quot;Bleeker&quot; bundle:nil];<br />	self.badBleeker1 = tmpController;<br />	[tmpController release];<br />	srandom(time(NULL));<br />	rL = random() % CASES_LARGEUR;<br />	rH = random() % CASES_HAUTEUR;<br />	if (leLaby[rL][rH] == 0)<br />	{<br />		self.badBleeker1.a = rH;<br />		self.badBleeker1.b = rL;<br />	}<br />	else <br />	{<br />		self.badBleeker1.a = 2;<br />		self.badBleeker1.b = 9;<br />	}<br />	self.badBleeker1.isBad = YES;<br />	self.badBleeker2.directionBleeker = 1;<br />	[self.badBleeker1 randomDestination];<br />	<br />	tmpController = [[MyBleeker alloc] initWithNibName:@&quot;Bleeker&quot; bundle:nil];<br />	self.badBleeker2 = tmpController;<br />	[tmpController release];<br />	rL = random() % CASES_LARGEUR;<br />	rH = random() % CASES_HAUTEUR;<br />	<br />	if (leLaby[rL][rH] == 0)<br />	{<br />		self.badBleeker2.a = rH;<br />		self.badBleeker2.b = rL;<br />	}<br />	else <br />	{<br />		self.badBleeker2.a = 4;<br />		self.badBleeker2.b = 9;<br />	}<br />	self.badBleeker2.isBad = YES;<br />	self.badBleeker2.directionBleeker = 1;<br />	[self.badBleeker2 randomDestination];<br />	<br />	tmpController = [[MyBleeker alloc] initWithNibName:@&quot;Bleeker&quot; bundle:nil];<br />	self.badBleeker3 = tmpController;<br />	[tmpController release];<br />	rL = random() % CASES_LARGEUR;<br />	rH = random() % CASES_HAUTEUR;<br />	if (leLaby[rL][rH] == 0)<br />	{<br />		self.badBleeker3.a = rH;<br />		self.badBleeker3.b = rL;<br />	}<br />	else <br />	{<br />		self.badBleeker3.a = 14;<br />		self.badBleeker3.b = 15;<br />	}<br />	self.badBleeker3.isBad = YES;<br />	self.badBleeker2.directionBleeker = 1;<br />	[self.badBleeker3 randomDestination];<br />	<br />	//init the other viewController<br />	ScoreController *tmpScore = [[ScoreController alloc] initWithNibName:@&quot;ScoreController&quot; bundle:nil];<br />	tmpScore.time = TIME;<br />	tmpScore.view.alpha = 0;<br />	self.monScore = tmpScore;<br />	[tmpScore release];<br />	<br />	BonusTimeController *tmpBT = [[BonusTimeController alloc] initWithNibName:@&quot;BonusTimeController&quot; bundle:nil];<br />	self.monBonusTime = tmpBT;<br />	[tmpBT release];<br />	<br />	ScoreHighlight *anHighlight = [[ScoreHighlight alloc] initWithNibName:@&quot;ScoreHighlight&quot; bundle:nil];<br />	anHighlight.view.alpha = 0;<br />	self.theHighlight = anHighlight;<br />	[anHighlight release];<br />	<br />	ScoreHighlight *anotherHighlight = [[ScoreHighlight alloc] initWithNibName:@&quot;DeathHighlight&quot; bundle:nil];<br />	anotherHighlight.view.alpha = 0;<br />	self.deathHighlight = anotherHighlight;<br />	[anotherHighlight release];<br />	<br />	DeathRect *tmpRect = [[DeathRect alloc] initWithNibName:@&quot;DeathRect&quot; bundle:nil];<br />	self.myRect = tmpRect;<br />	self.myRect.view.alpha = 0;<br />	[tmpRect release];<br />	<br />	//insert all the views<br />	[self.view insertSubview:self.monBonusTime.view atIndex:0];<br />	[self.view insertSubview:self.monBleeker.view atIndex:1];<br />	[self.view insertSubview:self.theHighlight.view atIndex:1];<br />	[self.view insertSubview:self.deathHighlight.view atIndex:1];<br />	[self.view insertSubview:self.myRect.view atIndex:1];<br />	[self.view insertSubview:self.monScore.view atIndex:1];<br />	NSLog(@&quot;retaincCount bonustime apres%i&quot;, [monBonusTime retainCount]);<br />	//fire all the timers needed<br />	liveOrDieTimer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(liveOrDie) userInfo:nil repeats:YES];<br />	getBonusTimer = [NSTimer scheduledTimerWithTimeInterval:0.001 target:self selector:@selector(getBonus) userInfo:nil repeats:YES];<br />	countingTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(counting) userInfo:nil repeats:YES];<br />	<br />	//delay the bonus timer only the first time<br />	[self performSelector:@selector(bonussss) withObject:nil afterDelay:2.0];<br />	<br />	//stop the bleeker<br />	dontMove = YES;<br />	NSLog(@&quot;retaincCount monbleeker %i&quot;, [monBleeker retainCount]);<br />	NSLog(@&quot;retaincCount scorehighlight %i&quot;, [theHighlight retainCount]);<br />	NSLog(@&quot;retaincCount deathhighlitgh %i&quot;, [deathHighlight retainCount]);<br />	NSLog(@&quot;retaincCount bonustime %i&quot;, [monBonusTime retainCount]);<br />	NSLog(@&quot;retaincCount deathrect %i&quot;, [myRect retainCount]);<br />	NSLog(@&quot;retaincCount monscore %i&quot;, [monScore retainCount]);<br />
    


    Le retainCount de monBonusTime est de 2 !!
    et celui de monBleeker de 2 aussi...
  • LexxisLexxis Membre
    19:07 modifié #8
    La vue parente ne fait-elle pas un retain sur la vue enfant nouvellement ajouté via insert: ou add: ?
    Apres l'ajout de la vue enfant dans la vue parent ne faudrait-il pas faire un release sur la vue enfant?
  • zoczoc Membre
    juin 2009 modifié #9
    dans 1244197641:

    Le retainCount de monBonusTime est de 2 !!
    et celui de monBleeker de 2 aussi...


    Ce qui est tout à  fait normal d'après ce que je lis (en supposant que toutes les propriétés auxquelles tu affectes ces objets ont l'attribut retain... Je vais prendre toutes les lignes qui concernent monBonisTime et mettre en commentaire le retain count...

    <br />...<br />BonusTimeController *tmpBT = [[BonusTimeController alloc] initWithNibName:@&quot;BonusTimeController&quot; bundle:nil]; // retainCount = 1.<br />self.monBonusTime = tmpBT; // retainCount = 2.<br />[tmpBT release]; // retainCount = 1.<br />...<br />[self.view insertSubview:self.monBonusTime.view atIndex:0]; // retainCount = 2. (*)<br />
    


    (*) Cela parait bizarre, mais en fait, les getters générés par @synthetise font un retain et autorelease sur l'objet qu'ils retournent (et donc self.monBonusTime augmente le compteur de 1).

    Ceci explique un retainCount à  2. Mais grâce à  l'autorelease, le retainCount sera décrémenté de 1 automatiquement dès que l'autorelease pool sera flushé (c'est à  dire lors de la prochaine exécution de la boucle de traitement des messages).

    Si tu vas consulter les mailings lists d'Apple dédiées au développement avec Cocoa, tu verras que de nombreux contributeurs te dirons que consulter la valeur du compteur pour s'assurer qu'il n'y a pas de leaks n'est pas la bonne manière de faire, car cette valeur à  un instant t ne donne aucune indication précise sur le fait qu'un objet va être détruit ou pas (notamment parce qu'on ne sait pas comment sont générés les properties).

    La seule solution, encore une fois, c'est de bien s'assurer que pour chaque retain/initXXXX/copy/mutableCopy effectué, il y a bien un release/autorelease.
  • zoczoc Membre
    juin 2009 modifié #10
    dans 1244199625:

    La vue parente ne fait-elle pas un retain sur la vue enfant nouvellement ajouté via insert: ou add: ?
    Apres l'ajout de la vue enfant dans la vue parent ne faudrait-il pas faire un release sur la vue enfant?


    Oui, la vue parente fait un retain sur la vue enfant, pas son controlleur.

    Donc non, il ne manque pas de release, parce que la vue enfant appartient à  son controlleur, et c'est donc à  ce controlleur de faire un release sur cette vue, et pas au controlleur parent du controlleur en question. (ca fait beaucoup de controlleurs, je sais :p)

    Du moment que dans le dealloc de la classe ou est extraite son code il fait bien un release de monBleek et autre controlleurs "fils", il n'y a pas de problème de leak.
  • zoczoc Membre
    19:07 modifié #11
    Maintenant, petite question: Le code que tu nous a collé, il n'est appelé qu'une fois, ou plusieurs fois  (pour la même instance) ?

    Parce que s'il est appelé plusieurs fois, il va leaker des vues à  chaque appel. En effet, les vues qui ont été ajoutées par addSubView lors de l'appel n-1 doivent obligatoirement être supprimées par removeSubView lors de l'appel n, puisque ces vues ont été "retenues" par la vue parente !

  • allianallian Membre
    19:07 modifié #12
    Merci merci encore Zoc !
    cela m'éclaire une fois de plus, je comprends maintenant pourquoi le retainCount passe à  deux mais du coup comment faire pour avoir un retainCount de 1 et qu'il soit donc releasé dans la méthode dealloc.
    C'est pas bon de faire comme je fais ?
  • allianallian Membre
    19:07 modifié #13
    Ce code est celui de ma classe Jeu disons, il est appelé une fois par partie. Le probleme est que pour le moment vu qu'il ne se dealloc pas à  chaque fois que je refais une partie, tout les objets restent en mémoire..
  • zoczoc Membre
    juin 2009 modifié #14
    dans 1244205057:

    Ce code est celui de ma classe Jeu disons, il est appelé une fois par partie. Le probleme est que pour le moment vu qu'il ne se dealloc pas à  chaque fois que je refais une partie, tout les objets restent en mémoire..


    Il manque donc un release sur un objet de classe Jeu quelque part, donc, et pas dans la classe Jeu elle même (ou alors j'ai pas compris ta phrase... Tu parles bien du dealloc de Jeu ?).

    Je crois qu'il y a confusion sur le fonctionnement de la méthode dealloc. Quand tu écris:

    [toto release]

    Tu décrémentes le compteur de référence de toto. Le compteur de référence des objets que toto possède n'intervient ABSOLUMENT PAS. Si le compteur de toto passe à  zéro, alors le message dealloc est envoyé à  toto. L'implémentation du dealloc de toto doit alors faire un release sur tous les objets que toto possède.

    Il n'y a vraiement aucune magie dans ce mécanisme. Chaque objet à  un compteur qui lui est propre, il n'y a pas de notion de parent/enfant avec le compteur des enfants qui influerait sur le compteur des parents.
  • allianallian Membre
    19:07 modifié #15
    ma classe Jeu est instancié ainsi par un controlleur qui gere toute mon appli.

    BleekerViewController *tmpController = [[BleekerViewController alloc] init];			<br />			self.theBleekerViewController = tmpController;	<br />			NSLog(@&quot;retainCount bleeker %i&quot;, [self.theBleekerViewController retainCount]);<br />			NSLog(@&quot;retainCount tmp %i&quot;, [tmpController retainCount]);<br />			[UIView beginAnimations:@&quot;View Flip&quot; context:nil];<br />			[UIView setAnimationDuration:1.5];<br />			[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];<br />			[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];<br />			[self.theMenuViewController.view removeFromSuperview];<br />			self.theMenuViewController = nil;<br />			NSLog(@&quot;retainCount menu %i&quot;, [self.theMenuViewController retainCount]);<br />			[self.view insertSubview:tmpController.view atIndex:0];<br />			[UIView commitAnimations];<br />			[tmpController release];			<br />			NSLog(@&quot;retainCount bleeker %i&quot;, [self.theBleekerViewController retainCount]);<br />			NSLog(@&quot;retainCount tmp %i&quot;, [tmpController retainCount]);<br />
    


    Les NSLog me sorte ceci

    2009-06-05 14:35:47.374 Bleeker[6426:20b] retainCount bleeker 2
    2009-06-05 14:35:47.374 Bleeker[6426:20b] retainCount tmp 2
    2009-06-05 14:35:47.375 Bleeker[6426:20b] dealloc menu
    2009-06-05 14:35:47.376 Bleeker[6426:20b] retainCount menu 0
    2009-06-05 14:35:47.385 Bleeker[6426:20b] retainCount bleeker 8
    2009-06-05 14:35:47.387 Bleeker[6426:20b] retainCount tmp 8

    Et apres pour le remove de la vue je fais ceci :

    WinnerController *tmpController = [[WinnerController alloc] initWithNibName:@&quot;YouWin&quot; bundle:nil];<br />	tmpController.score = theFinalScore;<br />	self.theWinnerViewController = tmpController;<br />	[UIView beginAnimations:@&quot;View Flip&quot; context:nil];<br />	[UIView setAnimationDuration:1.5];<br />	[UIView setAnimationCurve:UIViewAnimationCurveEaseInOut];<br />	[UIView setAnimationTransition:UIViewAnimationTransitionFlipFromLeft forView:self.view cache:YES];<br />	[self.theBleekerViewController.view removeFromSuperview];<br />	self.theBleekerViewController = nil;<br />	NSLog(@&quot;retainCount bleeker %i&quot;, [self.theBleekerViewController retainCount]);<br />	[self.view insertSubview:tmpController.view atIndex:0];<br />	[UIView commitAnimations];<br />	<br />	[tmpController release];<br />
    


    Et le retainCount de ce dernier est bien à  0 mais alors pourquoi il ne fais jamais appel à  sa méthode dealloc ????
Connectez-vous ou Inscrivez-vous pour répondre.