1

私はまだプログラミングに慣れていないので、これがばかげているならすみません。私は単純なゲームをプログラミングしていて、さまざまなメッセージをさまざまな間隔で送信するために複数のタイマーが必要なので、ゲームを作成するとき、次のように呼ばれます。

[self gameTimerValidate];
[self scoreTimerValidate];

- (void) gameTimerValidate
{
gameTimer = [NSTimer scheduledTimerWithTimeInterval:[myGame gIntervalSpeed] target:self selector:@selector(gameTimerInterval:) userInfo:nil repeats:YES];
}

- (void) scoreTimerValidate
{
scoreTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(scoreTimerInterval:) userInfo:nil repeats:YES];
}

I have the scoreTimer and gameTimer declared in my header file ("NSTimer *gameTimer;"). I invalidate the timers when pausing the game or completing the level, and call the above methods again when resuming the game or entering the next level, respectively.

I spent hours today trying to figure out why pausing the game would crash the application. After doing some debugging I noticed the retain count of gametimer was 0, and for scoretimer it was 2. Of course, I can't invalidate a timer with a retain count of 0, but I'm not sure how that came about.

Is there a specific way I must initialize two different NStimers? I been searching for hours on this to no avail...

4

5 に答える 5

5

NSTimer はトリッキーなクラスです。期待どおりに動作しません。

まず、タイマー インスタンスは、それらを初期化するオブジェクトによって最終的に保持されるのではなく、IIRC (NSRunLoop) によって保持されます。これは、タイマーを作成するオブジェクトがある場合、それを作成したオブジェクトとカスタム コード内の他のすべての参照を破棄しても、タイマーは引き続きアクティブであることを意味します。タイマーはメッセージを発信し続け、メッセージがどこから来ているのかわかりません。

次に、タイマーを停止/一時停止して再開することはできません。あなたがそれを無効にすると、それは死んでいます。

コードの残りの部分でタイマーを追跡する必要がないように、タイマーを管理するライト クラスを作成することをお勧めします。例えば

@interface SDL_SimpleTimerController : NSObject {
    NSTimer *currentTimer;
    NSTimeInterval theInterval;
    id theTargetObj;
    SEL theSelector;

    BOOL timerIsRunning;
}
@property (nonatomic,retain) NSTimer *currentTimer;
@property NSTimeInterval theInterval;
@property (nonatomic,retain) id theTargetObj;
@property SEL theSelector;
@property BOOL timerIsRunning;

-(SDL_SimpleTimerController *) initWithInterval:(NSTimeInterval)anInterval forTarget:(id)aTargetObj andSelector:(SEL)aSelector;

-(void) startTimer;
-(void) stopTimer;                                  
@end

@implementation SDL_SimpleTimerController
@synthesize currentTimer;
@synthesize theInterval;
@synthesize theTargetObj;
@synthesize theSelector;
@synthesize timerIsRunning;

-(SDL_SimpleTimerController *) initWithInterval:(NSTimeInterval) anInterval forTarget:(id) aTargetObj andSelector:(SEL) aSelector
{
    self=[super init];
    theInterval=anInterval;
    theTargetObj=aTargetObj;
    theSelector=aSelector;
    timerIsRunning=NO;

    return self;

}// end initWithInterval:   


-(void) startTimer{
    if (currentTimer) { 
        currentTimer=Nil;
    }
    currentTimer=[NSTimer scheduledTimerWithTimeInterval:theInterval  target:theTargetObj selector:theSelector userInfo:Nil repeats:YES];
    timerIsRunning=YES;
}//end startTimer

-(void) stopTimer{
    if (currentTimer) {
        [currentTimer invalidate];
        currentTimer=Nil;
    }
    timerIsRunning=NO;
}// end stopTimer

- (void)dealloc { 
    if (currentTimer) {
        [currentTimer release];
        currentTimer=Nil;
    }
    [theTargetObj release];
    theTargetObj=Nil;
    [super dealloc];
}
于 2009-10-18T16:15:02.110 に答える
2

タイマーは再利用できません。それらを無効にした後、それらは実行ループから削除され、それらの保持カウントが減らされ、次回のループで割り当てが解除されます。新しいものを作成するか、それらを無効にするのをやめる必要があります。

于 2009-10-18T01:32:15.350 に答える
0

[scoreTimer 保持] を実行している可能性がある場所と、gameTimer を複数回無効化 (または解放) している可能性がある場所を見つけようとする必要があると思います (retainCount をチェックした場所が後であった場合は、後者を行う必要があるだけです)。あなたは一度無効にしました)。次のいずれかを呼び出して、retainCount を増やすことはできません。

[self gameTimerValidate];
[self scoreTimerValidate];

一回以上。メモリ リークが発生し、同じ間隔で 2 つのタイマーが起動しますが、そのため、これらのタイマーのいずれかが保持カウントを高くすることはありません。

これら 2 つのインスタンス変数が保持されたプロパティであり、self.gameTimer = ... を使用してそれらを設定していた場合、余分な時間が保持されることがわかります。しかし、私が見るコードはあなたの問題を説明していません。

これら 2 つのタイマーのすべてのインスタンスを検索し、他に何が問題になっているのかを確認します。

1 つの提案として、次のように nil をチェックすることをお勧めします。

- (void) gameTimerValidate
{
    if (gameTimer == nil)
        gameTimer = [NSTimer scheduledTimerWithTimeInterval:[myGame gIntervalSpeed] target:self selector:@selector(gameTimerInterval:) userInfo:nil repeats:YES];
}

- (void) scoreTimerValidate
{
    if (scoreTimer == nil)
        scoreTimer = [NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(scoreTimerInterval:) userInfo:nil repeats:YES];
}
- (void) invalidateMyTimers {
    [gameTimer invalidate], gameTimer = nil;
    [scoreTimer invalidate], scoreTimer = nil;
}
于 2009-10-18T06:08:35.923 に答える
0

返信ありがとうございます。少し考えた後、TechZen が言ったことに似たアプローチを採用し、タイマーを BOOL 変数で実行し続け、その変数を使用して一時停止などのイベントをチェックします (つまり、ブール値の変更 vs タイマーの停止と開始)。

(このウェブサイトを初めて使用しましたが、回答の形式をまだ学んでいます)ありがとうございます!

于 2009-10-19T00:17:29.973 に答える
0

TechZenのライトクラスへの回答ですが、上記の対象オブジェクトは作成していないので解放すべきではないと思います(したがって所有しないでください)

 (void)dealloc { 
if (currentTimer) {
    [currentTimer release];
    currentTimer=Nil;
}
**[theTargetObj release];**
theTargetObj=Nil;
[super dealloc];

}

于 2010-01-17T16:06:52.503 に答える