【问题标题】:NSTimer crashes, when I call [Timer isValid] or [Timer invalidate]当我调用 [Timer isValid] 或 [Timer invalidate] 时,NSTimer 崩溃
【发布时间】:2011-04-11 04:17:19
【问题描述】:

我的 iPhone 应用中有两个 NSTimer。 DecreaseTimer 工作正常,但是当我调用 [timerCountSeconds isValid] 或 [timerCountSeconds invalidate] 时 TimerCountSeconds 崩溃。它们是这样使用的:

-(id)initialize { //Gets called, when the app launches and when a UIButton is pressed
 if ([timerCountSeconds isValid]) {
  [timerCountSeconds invalidate];
 } 
}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { //Gets called, when you begin touching the screen
 //....
 if ([decreaseTimer isValid]) {
   [decreaseTimer invalidate];
  }
 timerCountSeconds = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(runTimer) userInfo:nil repeats:YES];
 //....
}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {//Gets called, when you stop touching the screen(not if you press the UIButton for -(id)initialize)
 //...
 decreaseTimer = [NSTimer scheduledTimerWithTimeInterval:0.05 target:self selector:@selector(decrease) userInfo:nil repeats:YES];
 //...
}
-(void)comept3 { //Gets calles when you rubbed the screen a bit
    if ([timerCountSeconds isValid]) {
    [timerCountSeconds invalidate];
    }
}

我做错了什么? 你能帮帮我吗?

【问题讨论】:

  • 你初始化TimerCountSeconds和DecreaseTimer了吗?
  • 我在头文件中声明了它们...我在“touchesEnded”和“touchesBegan”方法中初始化了它们
  • 显示更多代码。此外,使用像 DecreaseTimer 这样的名称作为实例变量通常是一个非常糟糕的主意。以大写字母开头的名称用于类和结构。考虑使用 Apple 提倡的一致风格。
  • 好的,我改了变量名...我忘了输入“comept3”方法...这可能会帮助你帮助我;)

标签: iphone objective-c xcode crash nstimer


【解决方案1】:

您应该在invalidate 之后将NSTimer 对象设置为nil,因为invalidate 方法调用也执行release(根据Apple 文档)。如果您不这样做,则在其上调用诸如 isValid 之类的方法可能会导致您的崩溃。

【讨论】:

  • 双重投票 - 为这个问题搜索了几个小时......顺便说一句:如果在其他地方使用 isValid 检查它,保留 NSTimer 可能是非常明智的。如果不保留,它可能会在触发时自动释放(另见 Chuck 的回答)。
【解决方案2】:

很可能存储在该变量中的计时器已被释放。如果你想保留它任意长的时间,你需要保留它。

【讨论】:

  • 什么时候解除分配?那我什么时候必须保留它?
  • 当 runloop 决定它可以被释放时,它将被释放。如果你想保留它,你应该在将它存储在某个地方时保留它,就像任何其他对象一样。
  • 好吧,我想我明白了;)我在初始化后保留了它……现在它不再崩溃了:)对吗?
【解决方案3】:
 [objTimer retain];

那么它不会在任何时候崩溃。在初始化计时器后使用它,这样它就可以正常工作了......

【讨论】:

    【解决方案4】:

    您需要在主线程中设置计时器。 NSTimer 不会在后台线程中触发。

    • 对象:

      dispatch_async(dispatch_get_main_queue(), ^{
         _timer = [NSTimer scheduledTimerWithTimeInterval:delay target:self selector:@selector(YOUR_METHOD) userInfo:nil repeats:YES];
      });
      
    • 斯威夫特:

      dispatch_async(dispatch_get_main_queue()) {
         timer = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: "YOUR_METHOD", userInfo: nil, repeats: true)
      }
      

    【讨论】:

    • invalidate() 怎么样。我还需要在主线程中调用它吗?
    【解决方案5】:

    您需要在初始化时实际初始化TimerCountSecondsDecreaseTimer 成员。假设您的控制流程是:

    ...
    myObject = [[MyObject alloc] initialize];
    ...
    [myObject touchesBegan:...]
    ...
    [myObject touchesEnded:...]
    ...
    

    那么当你调用initializeTimerCountSeconds还没有被初始化,所以你在逻辑上这样做

    [<random pointer> isValid]
    

    这会崩溃。同样,第一次调用 touchesBegan 时,DecreaseTimer 无效。

    在您的初始化方法中,您需要在尝试使用任何内容之前实际初始化所有内容。

    您似乎也在泄漏计时器(touchesBegin 使计时器无效但不释放它)

    【讨论】:

    • 如果这些是实例变量,它们就不是“随机指针”——它们是 nil。
    【解决方案6】:
    -(void)StopScanTimer
    {
        if(scanTimer != nil)
        {
            [scanTimer invalidate];
            scanTimer = nil;
        }
    }
    
    -(void)StartScanTimer
    {
        [self StopScanTimer];
    
        float duration = 10.0f;
        scanTimer = [NSTimer timerWithTimeInterval:duration target:self     selector:@selector(OnScanTimerElapsed) userInfo:nil repeats:NO];
        [[NSRunLoop mainRunLoop] addTimer:scanTimer forMode:NSRunLoopCommonModes];
        [scanTimer retain];
    }
    
    -(void) OnScanTimerElapsed
    {
        // Do something
    }
    

    如果你删除“[scanTimer retain];”,当你调用invalidate时它会崩溃。 如果您想重新使用它,请始终保留计时器。

    【讨论】:

      猜你喜欢
      • 2013-09-24
      • 1970-01-01
      • 2012-07-11
      • 2012-01-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多