当我调用 [Timer isValid] 或 [Timer invalidate] 时,NSTimer 崩溃

Posted

技术标签:

【中文标题】当我调用 [Timer isValid] 或 [Timer invalidate] 时,NSTimer 崩溃【英文标题】:NSTimer crashes, when I call [Timer isValid] or [Timer invalidate] 【发布时间】: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”方法...这可能会帮助你帮助我;) 【参考方案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时它会崩溃。 如果您想重新使用它,请始终保留计时器。

【讨论】:

以上是关于当我调用 [Timer isValid] 或 [Timer invalidate] 时,NSTimer 崩溃的主要内容,如果未能解决你的问题,请参考以下文章

ASP.NET 4.5 自定义验证属性。 IsValid() 调用太晚了

调用线程无法访问此对象 - Timer [重复]

python线程threading.Timer源码解读

[转]验证发生前无法调用 Page.IsValid。应在 CausesValidation=True 且已启动回发的控件

Springboot+JPA(Hibernate)+Oracle AbstractMethodError 未定义或继承 isValid(int) 的实现

Erlang timer:sleep(1000) 导致线程死机