我仍然是编程的新手,所以如果这很愚蠢,请原谅。我正在编写一个简单的游戏,并且需要多个计时器以不同的间隔发送不同的消息,因此在创建游戏时,会调用以下内容:
[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,而得分记录为2.当然,我不能使保留计数为0的计时器无效,但我不确定是怎么发生的。< / p>
我必须以特定的方式初始化两个不同的NStimers吗?我一直在寻找数小时无济于事......
答案 0 :(得分:4)
NSTimer是一个棘手的课程。它的行为并不像你期望的那样。
首先,初始化它们的对象最终不会保留定时器实例,而是由IIRC(NSRunLoop)保留。这意味着如果您有一个创建计时器的对象,即使您销毁了创建它的对象和自定义代码中的所有其他引用,计时器也将继续处于活动状态。计时器将继续发射消息,你不知道他们来自哪里。
其次,您无法停止/暂停和恢复计时器。当你使它失效时,它已经死了。
我建议创建一个轻量级,为您管理计时器,这样您就不必在其余代码中跟踪它。 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];
}
答案 1 :(得分:2)
计时器不可重复使用。使它们失效后,它们将从运行循环中删除,并且它们的保留计数会减少,从而导致下次循环时释放它们。您要么必须创建新的,要么停止使它们失效。
答案 2 :(得分: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;
}
答案 3 :(得分:0)
感谢回复,在给出一些想法之后我会采用类似于TechZen所说的方法,并且只是让计时器运行BOOL变量,并使用该变量来检查诸如暂停和这样(即改变布尔值与停止和启动定时器)。
(也是我第一次使用这个网站,仍在学习答案的格式)再次感谢!
答案 4 :(得分:0)
在回复TechZen的灯光类时,我认为你不应该发布上面的目标对象,因为你没有创建它(因此不拥有它)
(void)dealloc {
if (currentTimer) {
[currentTimer release];
currentTimer=Nil;
}
**[theTargetObj release];**
theTargetObj=Nil;
[super dealloc];
}