我在Xcode 6中制作了一个游戏,使用了几个NSTimers用于不同的事情,比如记分器计时器,倒数计时器,以及移动我的物体。问题是,有时(似乎)NSTimers停止了半秒钟,这使它看起来像是滞后。示例:当角色移动时,它会停止一小秒然后继续移动。它发生得如此之快,但它是显而易见的,它让我很烦。我希望它完全顺利。任何帮助,将不胜感激!
答案 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)
有几点想法:
如果计时器处理有一点延迟,最可能的问题是你有阻塞主队列的东西。仔细查看您的代码,看看是否可以找到任何可能阻塞主队列的内容。
您实际上可以使用Instruments在应用中查找线程可能被阻止的位置。如果我没记错的话,WWDC 2112视频Building Concurrent User Interfaces on iOS显示了工具可以找到您的应用被阻止的位置。它有点过时了,但找到主线程块仍然适用的技术。
这不太可能,但您可能需要考虑检查运行计时器的运行循环模式。例如,默认值:
[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];
这导致计时器在特定类型的动画期间不易受某些延迟的影响。当你看到用户界面的延迟时,它只取决于你的应用程序正在做什么。
使用计时器更新动画时,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。此时还有其他更长时间讨论的主题,但这可能是介绍该主题的好地方(尽管该视频绝大部分是关于其他主题的)。