NSTimer的动作方法没有被调用

时间:2011-11-21 18:41:08

标签: objective-c ios cocoa-touch nstimer

我有以下代码来创建NSTimer,每次触发时都应更新标签:

.h文件

@interface Game : UIViewController
{
    NSTimer *updateTimer;

    UILabel *testLabel;
    int i;
}
-(void)GameUpdate;

@end

.m文件

@implementation Game

-(void)GameUpdate
{
    i++;
    NSString *textToDisplay = [[NSString alloc] initWithFormat:@"Frame: %d", i];
    [testLabel setText:textToDisplay];
    NSLog(@"Game Updated");
}

- (void)viewDidLoad
{
    [super viewDidLoad];

    [[UIApplication sharedApplication] setStatusBarHidden:YES animated:NO];
    updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];
}

//other methods (viewDidUnload, init method, etc.)

@end

当我运行它时,顶部会出现一个标签,显示“0”但不会改变。它让我相信我错过了NSTimer如何设置的内容。我错过了什么?

我使用断点和(如您所见)记录以查看方法是否实际运行,而不是其他一些错误。

4 个答案:

答案 0 :(得分:14)

我遇到了类似的问题,它有一个与运行循环相关的不同根本原因。值得注意的是,当您使用代码安排Timer时:

updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];

计时器将通过当前主题的runLoop进行安排。在你的情况下,因为你在viewDidLoad中进行了这个调用,它是主线程,所以你很高兴。

但是,如果您使用主线程以外的线程安排计时器,它将在该线程的runLoop上进行调度,而不是主线程。哪个没问题,但是在辅助线程上,你负责创建和启动初始运行循环,所以如果你还没有这样做 - 你的回调永远不会被调用。

解决方案是为你的辅助线程启动runLoop,或者将你的计时器启动分配给主线程。

发送:

dispatch_async(dispatch_get_main_queue(), ^{
    updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];
});

启动runloop:

使用您选择的API创建线程后,调用CFRunLoopGetCurrent()为该线程分配初始运行循环。将来对CFRunLoopGetCurrent的任何调用都将返回相同的运行循环。

CFRunLoopGetCurrent();
updateTimer = [NSTimer scheduledTimerWithTimeInterval:0.01428 target:self selector:@selector(GameUpdate) userInfo:nil repeats:YES];

答案 1 :(得分:7)

你的回调必须有这个签名:

-(void)GameUpdate:(NSTimer *)timer

这在文档中明确说明。设置计时器时@selector()引用应为@selector(GameUpdate:)(注意尾随:)。

试试。

答案 2 :(得分:1)

万一有人偶然发现这一点,我想指出这一点:

[[NSString alloc] initWithFormat:@"Frame: %d", i];

需要内存管理。

安全地替换为:

[NSString stringWithFormat:@"Frame: %d", i];

效果相同但不需要内存管理。

P.S。在撰写本文时,我无法对原帖发表评论,因此我将其添加为答案。

编辑:正如adam waite所指出的那样,这与ARC的广泛使用不再相关。

答案 3 :(得分:0)

我与 NSTimer 有一点不同的问题 - 在UITableView滚动期间忽略了预定的方法调用。 计时器已从主线程启动。明确地向主运行循环添加计时器解决了问题。

String res = "";
for(int i = 0; i < dup; i++) {
    res = res + ".";
}
return res;

此处找到解决方案https://stackoverflow.com/a/2716605/1994889

UPD [[NSRunLoop mainRunLoop] addTimer:playbackTimer forMode:NSRunLoopCommonModes]; 更适合更新用户界面。 根据官方文档,CADisplayLink是:

  

表示绑定到显示vsync的计时器的类。

可以轻松实现,如:

CADisplayLink

并删除

  playbackTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateUI)];
 [playbackTimer addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];