2010-10-19 6 views
3

Quelle est la manière correcte de libérer un NSTimer dans ma méthode dealloc? Il a été créé avec le code suivant?Manière correcte de libérer NSTimer?

-(void)mainTimerLoop { 

    mainTimer = [NSTimer scheduledTimerWithTimeInterval:1/10 
                target:self 
               selector:@selector(gameLoop) 
               userInfo:nil 
               repeats:YES]; 
} 

Merci

Répondre

-1

vous avez une très bonne réponse au sujet de NSTimer ici How do I use NSTimer? là, ils parlent de stoping une répétition NSTimer faisant

[myTimer invalidate]; 
+14

C'est faux - et c'est un très mauvais conseil! La gestion de la mémoire avec un NSTimer est très particulière: elle conserve la cible et ne la libère pas tant qu'elle n'est pas invalidée. S'il crée un NSTimer répétitif avec self comme cible, par conséquent, le temporisateur retient self et donc le dealloc ne sera pas appelé avant * que * le timer ne soit invalidé. Donc ne dites pas "arrêtez le timer * dans * votre méthode dealloc". L'astuce consiste à trouver un endroit pour arrêter le timer * avant * la méthode dealloc. – matt

+0

Down-voté pour les mêmes raisons que données par matt. Besoin d'invalider et de libérer une minuterie en dehors de dealloc. – theLastNightTrain

-3

Vous n'êtes pas obligé de le libérer car il sera autoreleased. Tout ce qui est créé par une méthode pratique (c'est-à-dire vous n'appelez pas alloc) est la responsabilité de la fonction appelée pour gérer la mémoire, ce qui signifie généralement qu'elle appellera autorelease sur l'objet qu'elle crée avant de le renvoyer.

J'attribuerais cependant le temporisateur à une propriété avec le mot clé retain afin de m'assurer qu'il ne soit pas désaffecté sur vous. Généralement, les objets auto-libérés sont désaffectés dans la boucle d'événements s'ils n'ont pas de retenue.

+1

Ceci est faux. Le minuteur est conservé par le NSRunLoop sur lequel il est programmé. Il n'est pas libéré tant qu'il n'est pas invalidé. – matt

1

valide de NSTimer est retenue par la boucle d'exécution, qui, si elle répète, sera pour toujours ou jusqu'à ce que vous l'invalidiez. Vous ne devriez pas le publier car, dans votre exemple de code, vous ne l'avez pas conservé explicitement. Si vous l'invalidez, il ne sera plus retenu par la boucle d'exécution et sera auto-libéré. Ceci peut être OK pour une minuterie à répétition, mais elle est dangereuse pour une minuterie à usage unique, car elle pourrait être libérée avant que vous y accédiez pour voir si elle est valide et/ou essayer de l'invalider (ce qui conduirait à à un accident d'application mauvaise accès). Par conséquent, si vous envisagez, d'une manière ou d'une autre, d'examiner une variable de temporisateur après sa création (y compris pour la vérifier, l'invalider et/ou la libérer), il peut être judicieux de la conserver explicitement dans votre application, puis relâchez-le et réglez-le à zéro après il est invalide et vous avez fini avec elle.

Vous pouvez le libérer et le définir à zéro dans une instruction si vous le déclarez comme propriété de conservation. Ensuite, vous pouvez écrire:

self.timer = nil; 
+0

Vous êtes sûr que si vous définissez un objet sur "nil", vous l'appelez dans obj-c? – keith

15

La façon dont vous le faites, vous ne serez jamais touché dealloc. Une minuterie conserve sa cible. Dans ce cas, cela signifie que la minuterie vous a retenu. Il ne vous libèrera pas avant d'être invalidé. Depuis que vous avez créé le minuteur, vous devez également l'invalider à un certain moment avant dealloc, car la fonction de temporisation empêchera l'objet d'être dealloc.

Vous avez deux options:

  • trouver un autre endroit pour invalider la minuterie (vue se offscreen, l'application se termine, ce que vous avez)
  • ensemble quelque chose d'autre que l'objectif de la minuterie.

À titre d'exemple de ce dernier:

@interface GameLoopTimerTarget : NSObject { 
    id owner; /* not retained! */ 
} 
- (id)initWithOwner:(id)owner; 
- (void)timerDidFire:(NSTimer *)t; 
@end 

@implementation GameLoopTimerTarget 
- (id)initWithOwner:(id)owner_ { 
    self = [super init]; 
    if (!self) return nil; 

    owner = owner_; 
    return self; 
} 

- (void)timerDidFire:(NSTimer *)t { 
#pragma unused (t) 
    [owner performSelector:@selector(gameLoop)]; 
} 
@end 

/* In your main object… */ 
/* assume synthesized: 
@property (retain, NS_NONATOMIC_IPHONE_ONLY) GameLoopTimer *mainTimerTarget; */ 
- (void)mainTimerLoop { 
    self.mainTimerTarget = [[[GameLoopTimerTarget alloc] initWithOwner:self] autorelease]; 
    mainTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0 target:self.mainTimerTarget selector:@selector(timerDidFire:) userInfo:nil repeats:YES]; 
} 

- (void)dealloc { 
    /* other stuff */ 
    [timer invalidate], timer = nil; 
    [mainTimerTarget release], mainTimerTarget = nil; 
    /* more stuff */ 
    [super dealloc]; 
} 

Remarquez comment l'intervalle de temps est 1.0/10.0 - cela pourrait aussi être écrit 0.1, mais il ne peut pas être écrit 1/10, comme cette division va tronquer 0.0.

Notez également comment cela brise le cycle de conserver:

  • Vous et votre minuterie la cible de conserver la minuterie.
  • Vous avez cliqué sur dealloc à l'heure normale.
  • Vous invalidez alors le temporisateur et relâchez la temporisation.
  • La cible du temporisateur est ensuite désallouée.
+0

Merci beaucoup, c'était vraiment utile! –

Questions connexes