AppDelegate中的NSTimer永远不会触发

时间:2014-05-02 23:47:10

标签: ios nstimer ios7.1

我需要任意启动一个NSTimer,每隔N秒重复一次,直到我停止它为止。 不重复。

我尝试了SO解决方案描述了hereherehere。没有一个被接受的答案正在发挥作用。

这就是我所做的。我的UIViewController正在调用:

[AppDelegate doStartMessageRefresh];

在AppDelegate.m中:

#define MESSAGE_REFRESH_INTERVAL_SECONDS    3.0
@property (nonatomic, retain, strong) NSTimer *messageRefreshTimer;
- (void) doInvokerForDoStartMessageRefresh
{
    NSLog(@"%s", __FUNCTION__);
    NSLog(@"%@", [self.messageRefreshTimer fireDate]);
}
- (void) doStartMessageRefresh {
    NSLog(@"%s", __FUNCTION__);
    /*
    self.messageRefreshTimer = [NSTimer scheduledTimerWithTimeInterval:MESSAGE_REFRESH_INTERVAL_SECONDS
                                                                target:self
                                                              selector:@selector(doInvokerForDoStartMessageRefresh)
                                                              userInfo:nil
                                                               repeats:YES];
    */
    self.messageRefreshTimer = [NSTimer timerWithTimeInterval:MESSAGE_REFRESH_INTERVAL_SECONDS
                                                       target:self
                                                     selector:@selector(doInvokerForDoStartMessageRefresh)
                                                     userInfo:Nil
                                                      repeats:YES];
    // [self.messageRefreshTimer fire];
    //[[NSRunLoop currentRunLoop] addTimer:self.messageRefreshTimer forMode:NSRunLoopCommonModes];
    [[NSRunLoop currentRunLoop] addTimer:self.messageRefreshTimer forMode:NSDefaultRunLoopMode];
}

打印出的唯一日志行是:

2014-05-02 16:34:20.248 myappnamehere[9977:6807] -[AppDelegate doStartMessageRefresh]

如果我取消注释行[self.messageRefreshTimer fire];它运行一次。

如果我将选择器表示为:@selector(doInvokerForDoStartMessageRefresh :)并手动触发,我会收到无法识别的选择器

3 个答案:

答案 0 :(得分:3)

您的方法签名错误。它应该是doInvokerForDoStartMessageRefresh:doInvokerForDoStartMessageRefresh:(NSTimer *)timer,而不是doInvokerForDoStartMessageRefresh。请注意,:是方法名称的一部分!没有结肠,这是一种完全不同的方法。

以下是设置重复计时器的方法:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(timer:) userInfo:nil repeats:YES];

  return YES;
}

- (void)timer:(NSTimer *)timer
{
  NSLog(@"%@", [NSDate date]);
}

此外,如果您想在计算机中存储计时器,而不是这样做:

@property (nonatomic, retain, strong) NSTimer *messageRefreshTimer;

这样做:

@property NSTimer *messageRefreshTimer;

答案 1 :(得分:3)

您注意到,如果您将计时器发送到主队列,它可以工作:

dispatch_async(dispatch_get_main_queue(), ^{
    self.messageRefreshTimer = [NSTimer scheduledTimerWithTimeInterval:MESSAGE_REFRESH_INTERVAL_SECONDS
                                                                target:self
                                                              selector:@selector(refreshMessage:)
                                                              userInfo:nil
                                                               repeats:YES];
});

这表明您从主线程以外的某个线程调用此方法,或者更确切地说,没有运行循环的线程。也许你是从一些不使用主队列作为完成处理程序的异步方法的完成块中调用它的。也许您已手动将其分配到某个后台队列。

无论如何,应该在具有运行循环的线程上调度NSTimer,并将其分派给主运行循环是一种简单的方法。另一种方法是创建但不安排计时器(使用timerWithTimeInterval),然后手动将计时器添加到主运行循环:

self.messageRefreshTimer = [NSTimer timerWithTimeInterval:MESSAGE_REFRESH_INTERVAL_SECONDS
                                                   target:self
                                                 selector:@selector(refreshMessage:)
                                                 userInfo:nil
                                                  repeats:YES];
[[NSRunLoop mainRunLoop] addTimer:self.messageRefreshTimer forMode:NSDefaultRunLoopMode];

注意,我已经简化了选择器的方法名称,您可以使用以下方法:

- (void)refreshMessage:(NSTimer *)timer
{
    // refresh your message here
}

但是,如果您想在主线程以外的线程上运行计时器,您还可以创建一个GCD计时器源,它不需要运行环路来运行:

dispatch_queue_t  queue = dispatch_queue_create("com.domain.app.messageRefreshTimer", 0);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), MESSAGE_REFRESH_INTERVAL_SECONDS * NSEC_PER_SEC, 1ull * NSEC_PER_SEC);

dispatch_source_set_event_handler(timer, ^{
    [self refreshMessage];
});

dispatch_resume(timer);

如果您故意不想在主队列上运行计时器,我只会使用这种最终方法。

答案 2 :(得分:1)

使用GCD解决了我的问题。我在dispatch_async中包含了 doStartMessageRefresh()的主体(dispatch_get_main_queue(),^ {...}); ,NSTimer现在正在运行并且每隔N秒调用doInvokerForDoStartMessageRefresh()。

无论出于何种原因,此代码有效:

- (void) doInvokerForDoStartMessageRefresh:(NSTimer *) theTimer
{
    NSLog(@"%s", __FUNCTION__);
    NSLog(@"%@", [self.messageRefreshTimer fireDate]);
    NSLog(@"%@", [theTimer fireDate]);
}
- (void) doStartMessageRefresh {
    NSLog(@"%s", __FUNCTION__);

    dispatch_async(dispatch_get_main_queue(), ^{
        self.messageRefreshTimer = [NSTimer scheduledTimerWithTimeInterval:MESSAGE_REFRESH_INTERVAL_SECONDS
                                                                    target:self
                                                                  selector:@selector(doInvokerForDoStartMessageRefresh:)
                                                                  userInfo:nil
                                                                   repeats:YES];
     });
}

我仍然想知道为什么需要GCD。