简体   繁体   English

AppDelegate中的NSTimer从不触发

[英]NSTimer in AppDelegate never fires

I need to start an NSTimer arbitrarily that will repeat every N seconds until I stop it. 我需要任意启动一个NSTimer,它将每N秒重复一次,直到我停止它为止。 It's not repeating. 不重复了。

I've tried SO solutions described here , here and here . 我已经 在这里这里这里 尝试过SO解决方案 None of the the accepted answers are working. 接受的答案均无效。

Here's what I've done. 这就是我所做的。 My UIViewController is calling: 我的UIViewController正在调用:

[AppDelegate doStartMessageRefresh];

In AppDelegate.m: 在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];
}

The only log line that's printing out is: 打印出来的唯一日志行是:

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

If I uncomment the line [self.messageRefreshTimer fire]; 如果我取消注释该行[self.messageRefreshTimer fire]; it runs once. 它运行一次。

If I formulate the selector like: @selector(doInvokerForDoStartMessageRefresh:) and manually fire, I get an unrecognized selector sent 如果我将选择器公式化为:@selector(doInvokerForDoStartMessageRefresh :)并手动触发,则会收到无法识别的选择器

Your method signature is wrong. 您的方法签名错误。 It should be doInvokerForDoStartMessageRefresh: and doInvokerForDoStartMessageRefresh:(NSTimer *)timer , instead of doInvokerForDoStartMessageRefresh . 应该是doInvokerForDoStartMessageRefresh:doInvokerForDoStartMessageRefresh:(NSTimer *)timer ,而不是doInvokerForDoStartMessageRefresh Note that the : is part of the method name! 注意:是方法名称的一部分! Without the colon it's a totally different method. 没有冒号,则是完全不同的方法。

Here is how you setup a repeating timer: 这是设置重复计时器的方法:

- (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]);
}

Also, if you want to store a timer in a property, instead of doing this: 另外,如果要在属性中存储计时器,请不要这样做:

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

Just do this: 只要这样做:

@property NSTimer *messageRefreshTimer;

You noted that if you dispatch your timer to the main queue, it worked: 您注意到,如果将计时器分派到主队列,它将起作用:

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

This would suggest that you're calling this method from some thread other than the main thread, or more accurately, one without a run loop. 这表明您是从主线程之外的某个线程(或更准确地说,是没有运行循环的线程)调用此方法。 Perhaps you called it from the completion block of some asynchronous method that doesn't use the main queue for its completion handler. 也许您是从某种异步方法的完成块中调用它的,该异步方法的完成处理程序不使用主队列。 Perhaps you had manually dispatched it to some background queue. 也许您已将其手动分派到某个后台队列。

Regardless, NSTimer should be scheduled on a thread with a run loop, and dispatching this to the main run loop is an easy way to do that. 无论如何,应将NSTimer安排在具有运行循环的线程上,并将其分派到主运行循环是一种简便的方法。 Another approach is to create, but not schedule, the timer (using timerWithTimeInterval ) and then manually add the timer to the main run loop: 另一种方法是创建而不是安排计时器(使用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];

Note, I've simplified the method name of the selector, and you'd have a method like: 请注意,我已经简化了选择器的方法名称,并且您有一个类似以下的方法:

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

If, however, you want to run a timer on a thread other than the main thread, you can also create a GCD timer source, which doesn't require a runloop to function: 但是,如果要在除主线程之外的其他线程上运行计时器,则还可以创建一个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);

I'd only use this final approach if you deliberate did not want to run the timer on the main queue. 如果您故意不想在主队列上运行计时器,那么我只会使用这种最终方法。

Using GCD solved my problem. 使用GCD解决了我的问题。 I wrapped the body of doStartMessageRefresh() in dispatch_async(dispatch_get_main_queue(), ^{...}); 我将doStartMessageRefresh()的主体包装在dispatch_async(dispatch_get_main_queue(),^ {...})中; and NSTimer is now running and calling doInvokerForDoStartMessageRefresh() every N sec. NSTimer现在正在运行,每隔N秒调用一次doInvokerForDoStartStartRefersh()。

For whatever reason, this code works: 无论出于何种原因,此代码都有效:

- (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];
     });
}

I'd still like to know why GCD was required here. 我仍然想知道为什么这里需要GCD。

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

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