将 NSTimer 的触发时间重置为从现在开始而不是上次触发

发布于 2024-08-08 06:28:39 字数 196 浏览 2 评论 0 原文

我有一个 NSTimer,它以 3 秒的间隔触发以减少值。当我执行增加该值的操作时,我想重新启动计时器,从该点开始计时 3 秒。

例如,如果我增加该值并且计时器将在 1 秒内触发,我想更改它并让计时器在 3 秒内触发。我可以使计时器无效并重新创建它吗?或者我可以使用 setFireDate: 来实现,使用当前日期并添加 3 秒的间隔?

I have a NSTimer that fires with an interval of 3 seconds to decrease a value. When I do an action that increases that value, I want to restart the timer to count 3 seconds from that point.

For example, if I increase the value and timer will fires in 1 second, I want to change that and make the timer fire in 3 seconds instead. Can I invalidate the timer and create it again? Or can I do it with setFireDate:, using the current date and adding an interval of 3 seconds?

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

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

发布评论

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

评论(5

柒夜笙歌凉 2024-08-15 06:28:39

是的,您可以使其无效。并再次创建它。

您还可以使用:

- (void) myTimedTask {
    // other stuff this task needs to do

    // change the variable varyingDelay to be 1s or 3s or...  This can be an instance variable or global that is changed by other parts of your app
    // weStillWantTimer is also a similar variable that your app uses to stop this recurring task

if (weStillWantTimer)
       [self performSelector:@selector(myTimedTask:) withObject:gameColor afterDelay:varyingDelay];

 }

您调用 myTimedTask 来启动重复任务。一旦启动,您可以使用variantDelay更改延迟或使用weStillWantTimer停止它。

Yes, you can invalidate it. And create it again.

You can also use:

- (void) myTimedTask {
    // other stuff this task needs to do

    // change the variable varyingDelay to be 1s or 3s or...  This can be an instance variable or global that is changed by other parts of your app
    // weStillWantTimer is also a similar variable that your app uses to stop this recurring task

if (weStillWantTimer)
       [self performSelector:@selector(myTimedTask:) withObject:gameColor afterDelay:varyingDelay];

 }

You call myTimedTask to start the recurring task. Once it is started, you can change the delay with varyingDelay or stop it with weStillWantTimer.

揽月 2024-08-15 06:28:39

我做了一些测试,结果发现重置 fireDate 比使计时器失效并重新创建大约快四倍。首先,我创建一个调用 doNothing 方法的计时器:

if (!testTimer) {
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0
                                                      target:self
                                                    selector:@selector(doNothing:)
                                                    userInfo:nil
                                                     repeats:NO];
    testTimer   = timer;
}

这是测试代码:

- (void) testInvalidatingTimer {
    for (int n = 0; n < 10000; n++) {
        [testTimer invalidate];
        testTimer = nil;

        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0
                                                          target:self
                                                        selector:@selector(doNothing:)
                                                        userInfo:nil
                                                         repeats:NO];
        testTimer   = timer;
    }
}

- (void) testResettingTimer {
    for (int n = 0; n < 10000; n++) {
        if ([testTimer isValid]) {
            testTimer.fireDate = [NSDate dateWithTimeIntervalSinceNow:3.0];
        }
    }
}

在 iPad Air 上运行该计时器会产生 0.198173 秒的 invalidatingTimer 和 0.044207 秒的 resettingTimer。如果性能是您的目标,我建议重置 fireDate。这也大大减少了编码工作。

I've done a little testing, and it turns out that resetting the fireDate is about four times faster than invalidating and re-creating the timer. First, I create a timer which calls the method doNothing:

if (!testTimer) {
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0
                                                      target:self
                                                    selector:@selector(doNothing:)
                                                    userInfo:nil
                                                     repeats:NO];
    testTimer   = timer;
}

Here is the test code:

- (void) testInvalidatingTimer {
    for (int n = 0; n < 10000; n++) {
        [testTimer invalidate];
        testTimer = nil;

        NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3.0
                                                          target:self
                                                        selector:@selector(doNothing:)
                                                        userInfo:nil
                                                         repeats:NO];
        testTimer   = timer;
    }
}

- (void) testResettingTimer {
    for (int n = 0; n < 10000; n++) {
        if ([testTimer isValid]) {
            testTimer.fireDate = [NSDate dateWithTimeIntervalSinceNow:3.0];
        }
    }
}

Running that on an iPad Air yields 0.198173 s for invalidatingTimer and 0.044207 s for resettingTimer. If performance is your target, I recommend to reset the fireDate. It is also quite a bit less coding effort.

ㄖ落Θ余辉 2024-08-15 06:28:39

使计时器无效并重新创建它。但是,请确保您不会使计时器无效并释放计时器,除非您确定需要这样做,因为运行循环会保留计时器,直到它们无效,然后自行释放它们。

在我看来,将 -performSelector 代码与计时器代码混合会导致目标方法的多次执行,因此我会远离这种情况。

Invalidate the timer and recreate it. However, make sure you don't invalidate and release the timer unless you are sure you need to since the run loop retains timers until they are invalidated and then releases them itself.

In my opinion mixing -performSelector code with timer code leads to multiple execution of the target methods, so I'd stay away from that.

白龙吟 2024-08-15 06:28:39

是的,使计时器失效并重新创建是可行的。如果你不增加自己的价值,这本质上就是你想做的事情:重置并重新开始。

Yes, invalidating and recreating the timer will work. It is essentially what you are wanting to do if you are not increasing your value: reset and start over.

明媚殇 2024-08-15 06:28:39

setFireDate: 文档提到更改触发日期相对昂贵,这表明最好销毁并重新创建计时器,除非您这样做很多。不过,为了便于讨论,我不久前提出了这个类别。我更喜欢这个,因为它封装了日期调整行为;计时器本身处理它,而不是它的所有者/控制器。不过,我没有这方面的任何性能数据。

@implementation NSTimer (WSSAdjustingFireDate)

- (void)WSSFireAdjustingFireDate
{
    [self fire];

    [self WSSSkipNextFireAdjustingFireDate];
}

- (void)WSSSkipNextFireAdjustingFireDate
{
    CFRunLoopTimerRef cfSelf = (__bridge CFRunLoopTimerRef)self;
    CFTimeInterval delay = CFRunLoopTimerGetInterval(cfSelf);
    CFRunLoopTimerSetNextFireDate(cfSelf, CFAbsoluteTimeGetCurrent() + delay);
}

@end

我使用 Core Foundation 函数只是为了避免创建 NSDate。过早优化?请把盐递过去。

The setFireDate: docs mention that changing the fire date is relatively expensive, suggesting that it may be better to destroy and recreate the timer unless you're doing so a lot. For the sake of argument, however, I whipped up this category a little while ago. I prefer this because it encapsulates the date adjustment behavior; the timer itself handles it, rather than its owner/controller. I don't have any performance data on this, though.

@implementation NSTimer (WSSAdjustingFireDate)

- (void)WSSFireAdjustingFireDate
{
    [self fire];

    [self WSSSkipNextFireAdjustingFireDate];
}

- (void)WSSSkipNextFireAdjustingFireDate
{
    CFRunLoopTimerRef cfSelf = (__bridge CFRunLoopTimerRef)self;
    CFTimeInterval delay = CFRunLoopTimerGetInterval(cfSelf);
    CFRunLoopTimerSetNextFireDate(cfSelf, CFAbsoluteTimeGetCurrent() + delay);
}

@end

I've used the Core Foundation functions just to avoid creating an NSDate. Premature optimization? Please pass the salt.

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