[1/2 résolu]releaser proprement

cyranocyrano Membre
décembre 2010 modifié dans Objective-C, Swift, C, C++ #1
bonsoir et joyeuses fetes,

comment releaser vous un objet qui est retenu par ses propres property ?

ex
<br />@interface BC_Zoo : NSObject &lt;NSStreamDelegate&gt; {<br />	.../...	<br />	NSTimer			*keepAlive;<br />	.../...<br />}<br /><br />.../...<br /><br />- (id)initWith....<br />{<br />	[super init];<br />	if (self) {<br /><br />		.../...		<br />		keepAlive = [[NSTimer scheduledTimerWithTimeInterval:15<br />												 target:self<br />											&nbsp;  selector:@selector(keepAlive)<br />											&nbsp;  userInfo:nil<br />												repeats:YES]retain];<br />	}<br />	<br />	return self;<br />}<br /><br />


la soluce a deux methodes est peu sastifaisante

invalidateKeepAlive puis release

ou surcharger release
<br />- (void)release<br />{<br />	NSLog(@&quot;invalide le timer&quot;);<br />	<br />	[keepAlive invalidate];<br />	<br />	[super release];<br />}<br />


mais release va etre appelé deux fois (meme pas sur que cela soit propre comme code  :()

bref comment faites vous?


Réponses

  • laudemalaudema Membre
    21:00 modifié #2
    Bonjour,

    Si tu n'as pas créé ton timer via alloc copy ni appliqué de retain (ce qui est le cas ici) tu n'as pas à  te soucier du release. En plus je connais la méthode - (void)dealloc mais pas cette release que tu as codée.
    Si tu fais un retain via les properties tu dois faire un release dans le dealloc mais ici tu ne sembles pas les utiliser sinon tu aurais écrit self.keepAlive = ..
  • cyranocyrano Membre
    21:00 modifié #3
    ce n'est pas le timer que je veux releaser, mais l'objet proprietaire du timer.

    il ne m'est pas possible de releaser simplement car le timer retient lui meme l'objet (cycle).

    a) il faut que j'invalide le timer (== il relache l'objet)
    b) que je release l'objet, qui releasera le timer.

    voila ca fonctionne, mais peut t'on faire plus simple?

    car je dois faire

    [objet invalidateTimers];
    [objet release];

    un simple

    [objet release] semble plus naturel ?
  • zoczoc Membre
    21:00 modifié #4
    dans 1293450615:

    un simple

    [objet release] semble plus naturel ?

    Sauf que tel que tu l'as écris, ça ne marchera pas, puisque, en imaginant par exemple tu appelles 2 fois retain sur cet objet, au premier release le timer sera invalidé, alors que l'objet, lui, ne sera pas détruit (puisque "retain count" de 1).


    D'une manière générale, il est fortement déconseillé de surcharger les méthodes de gestion mémoire. Alors certes, il faut invalider le timer "à  la main", mais c'est la façon de faire prônée par Apple.

  • AliGatorAliGator Membre, Modérateur
    21:00 modifié #5
    En gros tu as un problème de conception car tu as créé un "retain cycle" entre tes deux objets, non ?

    Moi ce que je ferais c'est que je ne ferai pas de "retain" sur le timer car dès que tu le schedule sur la RunLoop elle retient le Timer elle-même (voir le Programming Guide" sur les timers tout est expliqué comme d'hab) et dans le dealloc tu le invalidate (et la runloop qui le retenait va le releaser)

    En tout cas surtout pas surcharger la méthode "release" !!! S'il te vient l'idée de faire une chose pareille c'est que tu n'as pas lu assez de tutos sur la gestion mémoire !!
  • cyranocyrano Membre
    21:00 modifié #6
    Sauf que tel que tu l'as écris, ça ne marchera pas, puisque, en imaginant par exemple tu appelles 2 fois retain sur cet objet, au premier release le timer sera invalidé, alors que l'objet, lui, ne sera pas détruit (puisque "retain count" de 1).


    non, un simple release suffit

    ma surchage de "release" s'auto-appele lors de [timer invalidate], bon je te l'accorde ca ne me plais pas.

    et pas sur que cela soit correcte, meme si ca fonctionne.


    Moi ce que je ferais c'est que je ne ferai pas de "retain" sur le timer car dès que tu le schedule sur la RunLoop elle retient le Timer elle-même (voir le Programming Guide" sur les timers tout est expliqué comme d'hab) et dans le dealloc tu le invalidate (et la runloop qui le retenait va le releaser)


    je vais lire, mais pas de retain == mon timer est releaser

    j'ai opté pour la soluce avec les 2 methodes.

  • cyranocyrano Membre
    21:00 modifié #7
    je vais lire


    merci ali, c'est resolu
  • cyranocyrano Membre
    21:00 modifié #8
    c'est resolu


    un peu vite :(

    le problème est le même, le timer possède tjrs une ref sur l'objet (pour le "tarjet", je pense), donc mettre invalidate dans le dealloc ne sert a rien.

    il faut deux methodes.....
  • AliGatorAliGator Membre, Modérateur
    21:00 modifié #9
    Un release doit être balancé par un retain et un scheduledTimer... ou un scheduleOnRunLoop sur un NSTimer doit être balancé par un "invalidate" sur le même NSTimer.
    Je ne vois pas trop ce qui pose problème dans ton cas du coup.
    Le "init" sur ton objet n'est appelé qu'à  la création de l'objet, pas à  chaque "retain" sur ledit objet ; le "dealloc" sur ton objet est automatiquement appelé quand l'objet est détruit (tu n'appeles jamais le dealloc directement, surtout pas !) parce que son retainCount tombe à  zéro,et pas à  chaque release. Bref en mettant le "scheduledTimer..." dans le init il ne sera bien créé et programmé qu'à  la création, et en mettant le "invalidate" dans le dealloc il ne sera bien détruit qu'à  la destruction de ton objet. Et entre temps si tu fais des retain et release sur ton objet tu n'as pas à  toucher ton NSTimer, il sera toujours retenu par ton objet tant que l'objet ne sera pas détruit...

    Bref un cas classique de composition (au sens conception/architecture/UML). C'est vrai pour les NSTimer comme pour le reste d'ailleurs...
  • cyranocyrano Membre
    21:00 modifié #10
    <br />- (id)initWithAppController:(CocOthAppController *)controller<br />{<br />	[super init];<br />	if (self) {<br />		<br />		appController = controller; //simple assignation<br />		<br />		srandom(time(NULL));<br />		asker = random();<br />		<br />		date&nbsp; &nbsp; &nbsp; = 0; <br />		requests&nbsp; = [[NSMutableArray alloc] init];<br />		nRequests = 0;<br />		<br />		NSRunLoop *runLoop = [NSRunLoop currentRunLoop];<br />		keepAlive = [NSTimer scheduledTimerWithTimeInterval:15<br />													 target:self<br />												&nbsp;  selector:@selector(keepAlive)<br />												&nbsp;  userInfo:nil<br />													repeats:YES];<br />		<br />		[runLoop addTimer:keepAlive<br />				&nbsp; forMode:NSDefaultRunLoopMode];<br /><br />						<br />		[self open];<br />	}<br />	<br />	return self;<br />}<br />
    


    voila mon init

    donc keepAlive est une simple assignation, il n'y a aucun retain "explicite" sur le timer, il est retenu par la runLoop.

    il ne faut pas se fier au retainCount aveuglement, mais a la sortie de mon init il est egal a 2.

    si je commente le timer il est egale a 1.

    donc mettre [keepAlive invalidate] dans dealloc ne sert a rien, dealloc ne sera pas appelé (retainCount -1 = 1) et non 0.

    je pense que le timer retient son "target".

    faites l'essai bon sang. ;-)



  • cyranocyrano Membre
    21:00 modifié #11
    invalidate
    Stops the receiver from ever firing again and requests its removal from its run loop.

    - (void)invalidate
    Discussion
    This method is the only way to remove a timer from an NSRunLoop object. The NSRunLoop object removes and releases the timer, either just before the invalidate method returns or at some later point.

    ICI +++> If it was configured with target and user info objects, the receiver releases its references to those objects as well.

    Special Considerations
    You must send this message from the thread on which the timer was installed. If you send this message from another thread, the input source associated with the timer may not be removed from its run loop, which could prevent the thread from exiting properly.

    Availability
    Available in Mac OS X v10.0 and later.
  • cyranocyrano Membre
    21:00 modifié #12
    je pense meme qu'une solution pourrait etre

    <br />-(void)release<br />{<br />	if([keepAlive isValid])<br />		[keepAlive invalidate];<br />	<br />	[super release];<br />}<br />
    


    pourquoi pas? (ce n'est pas un troll)
  • mpergandmpergand Membre
    décembre 2010 modifié #13
    dans 1293486776:

    <br /><br />		<br />		NSRunLoop *runLoop = [NSRunLoop currentRunLoop];<br />		keepAlive = [NSTimer scheduledTimerWithTimeInterval:15<br />												target:self<br />												 selector:@selector(keepAlive)<br />												 userInfo:nil<br />												repeats:YES];<br />		<br />		[runLoop addTimer:keepAlive<br />				&nbsp; forMode:NSDefaultRunLoopMode];<br /><br />}<br />
    



    Tu installes deux fois le même timer !

    soit tu fais ça:
    <br />keepAlive = [NSTimer scheduledTimerWithTimeInterval:15<br />					 target:self<br />					selector:@selector(keepAlive)<br />				 userInfo:nil<br />					repeats:YES];<br />		<br />
    


    ou bien ça:
    <br />NSRunLoop *runLoop = [NSRunLoop currentRunLoop];<br />keepAlive = [[NSTimer alloc] initWithTimeInterval:15<br />					target:self<br />					selector:@selector(keepAlive)<br />					userInfo:nil<br />					repeats:YES];<br />[runLoop addTimer:keepAlive forMode:NSDefaultRunLoopMode];	<br />[keepTimer release];	<br />
    


    mais pas les deux à  la fois ...

    -(void)release<br />{<br />	if([keepAlive isValid])<br />		[keepAlive invalidate];<br />	<br />	[super release];<br />}
    

    C'est dangereux de faire ça !

    Pour moi, la meilleur solution est d'utiliser un objet intermédiaire comme target, et faire dans celui-ci un appel sur l'objet initial (BC_Zoo)

    ainsi dans le dealloc de BC_Zoo faire:
    <br />[keepAlive invalidate];<br />[timerTarget release];<br />
    

  • cyranocyrano Membre
    décembre 2010 modifié #14
    Tu installes deux fois le même timer !


    hein......


    soit tu fais ça:
    <br />keepAlive = [NSTimer scheduledTimerWithTimeInterval:15<br />					 target:self<br />					selector:@selector(keepAlive)<br />				 userInfo:nil<br />					repeats:YES];<br />		<br />
    



    qui retient le timer, il faut un retain.

    ou bien ça:
    <br />NSRunLoop *runLoop = [NSRunLoop currentRunLoop];<br />keepAlive = [[NSTimer alloc] initWithTimeInterval:15<br />					target:self<br />					selector:@selector(keepAlive)<br />					userInfo:nil<br />					repeats:YES];<br />[runLoop addTimer:keepAlive forMode:NSDefaultRunLoopMode];	<br />[keepAlive release];	<br />
    

    mais pas les deux à  la fois ...


    je fais ca non? le timer est retenu par la runLoop et je conserve une reference (pointeur) dessus.

    C'est dangereux de faire ça !


    oui, ali m'a fourni une reponse:

    mon release est a usage unique, puisque il invalide mon timer, deduit de ...

    Et entre temps si tu fais des retain et release sur ton objet tu n'as pas à  toucher ton NSTimer....


    la SEULE solution propre est la soluce des deux methodes.

    merci a vous



  • mpergandmpergand Membre
    décembre 2010 modifié #15
    La seule solution "propre" est de faire un wrapper sur NSTimer (voir fichier joint)

    Par rapport à  toi , dans mon exemple timer est remplacé par TimerWrapper
    et  BC_Zoo par Counter

    Un appui sur le bouton démarre/stoppe le compteur
    - (IBAction)startAction:(id)sender<br />{<br /><br />	if([sender state]==NSOnState)<br />		{<br />		counter=[[Counter alloc] initWithCounterView:counterView];<br />		}<br />	else<br />		{<br />		[counter release];<br />		}<br />}<br />
    


    [counter release] invalide le timer dans dealloc, donc uniquement si Counter est désalloué,
    c'est à  dire proprement  :)
Connectez-vous ou Inscrivez-vous pour répondre.