这个问题可能已经存在,但我找不到。我的问题本质上是这样的。如果我有一个重复的 NSTimer 执行的时间比计时器间隔长,是否会有一些颠簸导致应用程序崩溃?或者,在执行的任务完成之前,新的时间事件是否开始?
4 回答
由于 NSTimer 在创建它的运行循环上运行,我认为它永远无法重新输入它调用的方法。这个关于运行循环的文档证实了这一点(参见“定时器源”部分:
“类似地,如果在运行循环正在执行处理程序例程中间时触发计时器,则计时器将等待直到下一次通过运行循环调用其处理程序例程”
您总是可以只安排一个只发生一次的 nstimer,然后在函数完成时重新安排它。
- (void)myFunction {
......stuff that your method does
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:@selector(myFunction) userInfo:nil repeats:NO];
}
重复计时器总是根据计划的触发时间而不是实际的触发时间来调度自己。例如,如果定时器计划在特定时间触发,并且此后每 5 秒触发一次,则计划的触发时间将始终落在原始的 5 秒时间间隔上,即使实际触发时间延迟也是如此。如果触发时间被延迟到超过一个或多个预定触发时间,则计时器在该时间段内仅触发一次;然后在触发后重新安排计时器,以用于未来的下一个预定触发时间。
只要您避免启动一些异步作业,就可以了。如果异步调度任务的时间通常比定时器调用之间的间隔时间长,那么该队列可以得到备份。如果做动画,即使动画可能没有完成,计时器也会触发。
让我举两个例子。对于这两个示例,假设我们创建了一个每秒触发一次的计时器:
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0
target:self
selector:@selector(handleTimer:)
userInfo:@"tick"
repeats:YES];
第一个例子:假设我们有一些串行队列:
self.queue = [[NSOperationQueue alloc] init];
self.queue.maxConcurrentOperationCount = 1;
此外,假设我们有一个NSTimer
处理程序,它执行以下操作:
- (void)handleTimer:(NSTimer *)timer
{
NSLog(@"%s %@", __FUNCTION__, timer.userInfo);
[self.queue addOperationWithBlock:^{
NSLog(@"%s starting some slow process; has %d operations queued", __FUNCTION__, self.queue.operationCount);
// to simulate a slow process, let's just sleep for 10 seconds
sleep(10);
NSLog(@"%s done", __FUNCTION__);
}];
}
因为计时器每秒触发一次,并且因为计时器处理程序几乎立即返回(因为它所做的只是排队后台操作),所以当第一个排队操作(需要 10 秒)完成并且第二个操作开始时,有该后台队列上已经有 10 个操作。而当第二个后台操作完成时,当第三个操作开始时,有 19 个操作排队。它只会变得更糟,因为NSTimer
处理程序将继续被调用,比较慢的后台操作从队列中清除的速度更快。显然,如果处理程序在当前队列中同步执行所有操作,那么一切都很好,并且没有积压,没有NSTimer
.
第二个例子:这个问题的另一个例子是动画。让我们假设计时器处理程序方法正在执行类似以下的操作,它启动了一个 10 秒的动画来移动 a UIImageView
:
- (void)handleTimer:(NSTimer *)timer
{
NSLog(@"%s %@", __FUNCTION__, timer.userInfo);
[UIView animateWithDuration:10.0
animations:^{
self.imageView.frame = [self determineNewFrame];
}
completion:nil];
}
这不起作用(或者更准确地说,handleTimer
即使之前的动画没有完成,您也会看到计时器调用的后续调用)。如果你要这样做,你必须跟踪动画是否完成。您必须执行以下操作:
- (void)handleTimer:(NSTimer *)timer
{
NSLog(@"%s %@", __FUNCTION__, timer.userInfo);
if (!self.animating)
{
NSLog(@"%s initiating another animation", __FUNCTION__);
[UIView animateWithDuration:10.0
animations:^{
self.animating = YES;
self.imageView.frame = [self determineNewFrame];
}
completion:^(BOOL finished){
self.animating = NO;
}];
}
}
您要么必须做一些状态标志(比如我的布尔animation
标志)以防止在第一个动画完成之前出现额外的动画,要么只是不使用循环计时器,而只是在动画类方法的completion
块中启动另一个计时器。UIView