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];
}

我在头文件(“NSTimer *gameTimer;”)中声明了 scoreTimer 和 gameTimer。我在暂停游戏或完成关卡时使计时器失效,并分别在恢复游戏或进入下一个关卡时再次调用上述方法。

我今天花了几个小时试图弄清楚为什么暂停游戏会使应用程序崩溃。在做了一些调试之后,我注意到 gametimer 的保留计数为 0,而 scoretimer 为 2。当然,我不能使保留计数为 0 的计时器无效,但我不确定这是怎么发生的。

有没有一种特定的方法我必须初始化两个不同的 NStimer?我一直在寻找几个小时无济于事......

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];

不止一次。您会泄漏内存,并让两个计时器以相同的时间间隔触发,但您不会因此而让其中一个计时器具有更高的 retainCount。

如果这两个实例变量是保留属性,并且您使用 self.gameTimer = ... 设置它们,那么我可以看到它们被保留了额外的时间。但是我看到的代码并不能解释你的问题。

搜索这两个计时器的所有实例,看看还有什么东西可能会弄乱事情。

一个建议,您可能想检查 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 变量保持计时器运行,并使用该变量检查暂停等事件(即更改布尔值与停止和启动计时器)。

(也是我第一次使用这个网站,还在学习答案的格式)再次感谢!

于 2009-10-19T00:17:29.973 回答
0

回复 TechZen 的 light 类,我认为你不应该释放上面的目标对象,因为你没有创建它(因此不拥有它)

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

}

于 2010-01-17T16:06:52.503 回答