iPhone 4有一个绝对确定的方式来长期NSTimer火灾

我一直有我的NSTimers和背景select器的麻烦。 这是让我疯狂,需要很长时间来尝试每一个调整。 为了保持我的理智,以及后代cocoa程序员的理智,我问了这个问题:

有一个绝对100%的可靠的方法来让一个定时的,长期的计时器在晚些时候触发,而不pipe它是从后台线程,主线程等调用的吗?

似乎我一直都不得不一遍一遍地解决使用NSTimers的大多数类的相同的问题。 他们在短期testing期间工作,比方说,我设置定时器通过后台线程在10秒内触发。 它工作,因为仍然有运行循环运行。 但是,一旦我把火灾时间改变到了我真正想要的时间,比如15-30分钟,就有了沉默。 运行循环已经结束,我不知道如何处理这种情况。 什么都没有发生,几天后我发现了这样的错误,一旦我已经忘记了哪个计时器会对此负责。

目前,我正在用select器做一些非常非常难看的舞蹈,例如这里有一个testing方法(它似乎适用于10分钟的定时器):

//this is a test method to simulate a background task requesting a timer [self performSelectorInBackground:@selector(backgroundReminderLongTermTest:) withObject:nil]; //this is a method similar to the one that the background thread would be trying to invoke -(void)backgroundReminderLongTermTest:(id)sender { [self performSelectorOnMainThread:@selector(backgroundReminderFromMainThread:) withObject:nil waitUntilDone:NO]; } //this is a wrapper for the background method, I want the timer to be added to a thread with a run loop already established and running -(void)backgroundReminderFromMainThread:(id)sender { [playTimers addObject:[NSTimer scheduledTimerWithTimeInterval:1800 target:self selector:@selector(start:) userInfo:nil repeats:NO]]; } 

我喜欢不必担心用计划的定时器创build一个消息date对象的便利,但是我应该忘记它们并使用具有特定的消息date的定时器? 看起来,scheduledTimer对于短期任务来说效果很好,当运行循环已经存在时,但是在应用程序执行期间,我根本看不到这种types的bug。 有一次,看起来定时器正常的发射,但后来他们完全停止发射。

感谢您的帮助或澄清。 我正在寻找一种计划定时器的方法,而不必担心每次需要计划定时器时是否存在运行循环。 我想确定,只要应用程序正在运行,通过此方法安排的定时器将在未来可预测的点上启动。

只要你依赖使用scheduledTimerWithTimeInterval:...你不能达到你想要的:
定时器将总是绑定到调用线程的运行循环。

如果在消息调用的时候没有与该线程相关的运行循环,当方法返回时肯定会有一个运行循环,如果需要, -[NSRunLoop currentRunLoop]创build一个运行循环。

如果你不喜欢用于创build定时器的其他API,你可以做的是在NSTimer上提供一个类,它负责所有的调度等等,并且可以在其他项目中重用。

下面是这样一个类别的例子:

 #pragma mark - setting up a timer: + (NSTimer *)yourPrefix_mainLoopScheduledTimerWithTimeInterval:(NSTimeInterval)interval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat { NSTimer *timer = [self yourPrefix_timerWithTimeInterval:interval target:target selector:selector userInfo:userInfo repeats:shouldRepeat]; void (^scheduler)() = ^{ [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; }; if ([NSThread isMainThread]) { scheduler(); } else { // you should really be able to rely on the fact, that the timer is ready to roll, when this method returns dispatch_sync(dispatch_get_main_queue(), scheduler); } return timer; } // this is just a convenience for the times where you actually want an _unscheduled_ timer + (NSTimer *)yourPrefix_timerWithTimeInterval:(NSTimeInterval)interval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat { NSDate *fireDate = [NSDate dateWithTimeIntervalSinceNow:interval]; NSTimer *timer = [[self alloc] initWithFireDate:fireDate interval:interval target:target selector:selector userInfo:userInfo repeats:shouldRepeat]; return [timer autorelease]; } #pragma mark - tearing it down: - (void)yourPrefix_invalidateMainLoopTimer { [self yourPrefix_invalidateMainLoopTimerAsynchronous:NO]; } - (void)yourPrefix_invalidateMainLoopTimerAsynchronous:(BOOL)returnsImmediately { void (^invalidator)() = ^{ [self invalidate]; }; dispatch_queue_t mainQueue = dispatch_get_main_queue(); if (returnsImmediately) { dispatch_async(mainQueue, invalidator); return; } if (![NSThread isMainThread]) { dispatch_sync(mainQueue, invalidator); return; } invalidator(); } 

注意使用dispatch_sync之前的线程检查,因为…

dispatch_sync

讨论

调用此函数并将当前队列locking为死锁

(来自GCD参考 – 重点是我的)

NSTimers的无数问题之一是它们的运行循环依赖。 每个线程都有一个运行循环。 如果您在后台线程上计划一个计时器,它将被安排在该线程的运行循环中。 如果那个线程是短命的,哪个后台线程经常是这个计时器将会悄然死亡。

解决scheme是保证定时器运行在定时器触发时活动的线程上。 以我的经验来做这些专用背景定时器的最好方法是根本不使用NSTimer,而是使用GCD定时器。 比我更好的人编写了GCD动力计时器。 我个人比较喜欢迈克·阿什的文章和实现,并附有解释。

改用本地通知 。