NSTimer不时停止

时间:2014-11-05 23:57:21

标签: xcode nstimer

我在Xcode 6中制作了一个游戏,使用了几个NSTimers用于不同的事情,比如记分器计时器,倒数计时器,以及移动我的物体。问题是,有时(似乎)NSTimers停止了半秒钟,这使它看起来像是滞后。示例:当角色移动时,它会停止一小秒然后继续移动。它发生得如此之快,但它是显而易见的,它让我很烦。我希望它完全顺利。任何帮助,将不胜感激!

2 个答案:

答案 0 :(得分:1)

您可能需要尝试高分辨率计时器,例如Timer dispatch sources。起初看起来有点吓人,但实际上很容易使用。示例代码(带注释)

dispatch_source_t CreateDispatchTimer(uint64_t interval, uint64_t leeway, dispatch_queue_t queue , dispatch_block_t block) {
   dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
   if (timer) {
      // Use dispatch_time instead of dispatch_walltime if the interval is small
      dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), interval, leeway);
      dispatch_source_set_event_handler(timer, block);
      dispatch_resume(timer);
   }
   return timer;
}

void MyCreateTimer()
{
   dispatch_source_t aTimer = CreateDispatchTimer(30 * NSEC_PER_SEC, 1 * NSEC_PER_SEC, dispatch_get_main_queue(), ^{ 
       NSLog(@"Timer fired!");
   });

   // Keep a reference if you want to, say, stop it somewhere in the future
}

修改

在XCode中,如果您在编辑器中输入dispatch,它会建议一个名为dispatch_source timer - GCD: Dispatch Source (Timer)的代码段,它会生成计时器的模板代码。

答案 1 :(得分:1)

有几点想法:

  1. 如果计时器处理有一点延迟,最可能的问题是你有阻塞主队列的东西。仔细查看您的代码,看看是否可以找到任何可能阻塞主队列的内容。

    您实际上可以使用Instruments在应用中查找线程可能被阻止的位置。如果我没记错的话,WWDC 2112视频Building Concurrent User Interfaces on iOS显示了工具可以找到您的应用被阻止的位置。它有点过时了,但找到主线程块仍然适用的技术。

  2. 这不太可能,但您可能需要考虑检查运行计时器的运行循环模式。例如,默认值:

    [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
    

    这可能会在某些动画中暂停。您可以考虑使用更广泛的运行循环模式,例如:

    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(tick:) userInfo:nil repeats:YES];
    [[NSRunLoop mainRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    

    这导致计时器在特定类型的动画期间不易受某些延迟的影响。当你看到用户界面的延迟时,它只取决于你的应用程序正在做什么。

  3. 使用计时器更新动画时,NSTimer优于CADisplayLink。例如,定义一些属性:

    @property (nonatomic, strong) CADisplayLink *displayLink;
    @property (nonatomic) CFTimeInterval startTime;
    

    然后你可以编写代码来启动和停止显示链接:

    - (void)startDisplayLink
    {
        self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(handleDisplayLink:)];
        self.startTime = CACurrentMediaTime();
        [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
    
    - (void)stopDisplayLink
    {
        [self.displayLink invalidate];
        self.displayLink = nil;
    }
    
    - (void)handleDisplayLink:(CADisplayLink *)displayLink
    {
        CFTimeInterval elapsed = CACurrentMediaTime() - self.startTime;
    
        // update your UI, not on the basis of "this is called x times per second",
        // but rather, on the basis that `elapsed` seconds have passed
    }
    

    良好的动画代码中的关键是,您不仅假设您的例程将以指定的频率调用,而是根据elapsed秒的数量更新UI。这样,一个放慢几帧的慢速设备和一个快速的设备将产生相同的动画,后者将比前者更平滑。

    然而,在WWDC 2014视频中简要讨论了显示链接的优点 - Building Interruptible and Responsive Interactions。此时还有其他更长时间讨论的主题,但这可能是介绍该主题的好地方(尽管该视频绝大部分是关于其他主题的)。