繁体   English   中英

AppDelegate中的NSTimer从不触发

[英]NSTimer in AppDelegate never fires

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

我已经 在这里这里这里 尝试过SO解决方案 接受的答案均无效。

这就是我所做的。 我的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 :)并手动触发,则会收到无法识别的选择器

您的方法签名错误。 应该是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;

您注意到,如果将计时器分派到主队列,它将起作用:

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计时器源,该源不需要runloop即可起作用:

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);

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

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

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

- (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。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM