iphone 多个 NSTimer 的麻烦

发布于 2024-08-07 21:48:40 字数 816 浏览 5 评论 0原文

我对编程还是新手,所以如果这很愚蠢,请原谅。我正在编写一个简单的游戏,需要多个计时器以不同的时间间隔发送不同的消息,因此在创建游戏时,将调用以下内容:

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

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

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

有没有特定的方法我必须初始化两个不同的 NStimer?我已经为此搜索了几个小时但无济于事......

I'm still new to programming so excuse me if this is silly. I'm programming a simple game and require multiple timers to send different messages at different intervals, so when creating the game, the following is called:

[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...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

呆° 2024-08-14 21:48:40

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

NSTimer is a tricky class. It doesn't behave like you expect it to.

Firstly, the timer instances are not finally retained by the objects that initialize them but by IIRC, the NSRunLoop. This means that if you have an object that creates a timer, the timer will continue to be active even if you destroy the object that created it and all other references in your custom code. The timer will keep going along firing off messages and you have no clue where they're coming from.

Secondly, you can't stop/pause and resume a timer. When you invalidate it, it's dead.

I suggest creating a light class that will manage the timers for you so you don't have to keep track of it in the rest of your code. e.g.

@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];
}
掩于岁月 2024-08-14 21:48:40

定时器不可重复使用。在使它们无效后,它们将从运行循环中删除,并且它们的保留计数会递减,从而导致它们在下一次循环中被释放。您要么必须创建新的,要么停止使它们失效。

The timers are not reusable. After you invalidate them they are removed from the run loop and their retain count is decremented, resulting in their deallocation the next time through the loop. You'll either have to create new ones or stop invalidating them.

你爱我像她 2024-08-14 21:48:40

我认为你应该尝试找到你可能在哪里做[scoreTimer保留],以及你可能在哪里多次无效(或释放)gameTimer(你只需要做后者,如果你检查retainCount的地方是在之后)你已经失效一次了)。您无法通过

[self gameTimerValidate];
[self scoreTimerValidate];

多次调用其中任何一个来增加保留计数。您会泄漏内存,并且有两个计时器以相同的时间间隔触发,但因此您不会让其中一个计时器具有更高的保留计数。

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

搜索这两个计时器的所有实例,看看还有什么可能会造成混乱。

一个建议是,您可能需要检查是否为零,如下所示:

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

I think you should try to find where you might be doing a [scoreTimer retain], and where you might be invalidating (or releasing) gameTimer more than once (you only need to do the latter, if where you checked the retainCount, was after you had invalidated once). You can't increase the retainCount by calling either of

[self gameTimerValidate];
[self scoreTimerValidate];

more than once. You would leak memory, and have two timers firing at the same interval, but you wouldn't have one of those timers have a higher retainCount because of that.

If those two instance variables were retained properties, and you were setting them using self.gameTimer = ..., then I can see them getting retained an extra time. But the code I see doesn't explain your problem.

Search all instances of those two timers and see what else might be messing with things.

One suggestion, you might want to check for nil, like this:

- (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;
}
彼岸花ソ最美的依靠 2024-08-14 21:48:40

感谢您的回复,经过一番思考后,我将采用类似于 TechZen 所说的方法,并且只需使用 BOOL 变量保持计时器运行,并使用该变量来检查诸如暂停之类的事件(即更改布尔值与停止和启动计时器)。

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

Thanks for the replies, after giving it some thought I'm going to go with an approach similar to what TechZen said, and and just keep the timers running with a BOOL variable, and using that variable for checking events like pause and such (ie changing the boolean vs stopping and starting the timers).

(also my first time using this website, still learning the format of where the answers go) thanks again!

梦在夏天 2024-08-14 21:48:40

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

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

}

In reply to TechZen's light class, I think you should not release the target object above as you did not create it (therefore don't own it)

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

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文