[英]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.