code :因此,当计时器 id 停止并重新启动几次时,此代码工作正常,但是当我们重复相同的代码超过 30 次时会崩溃。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
aTimer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:@selector(ShowTime:) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] run];});
更新标签
-(void)ShowTime:(NSTimer *) timer{
NSTimeInterval now = [[NSDate date] timeIntervalSince1970];
double Timeinterval= (now - StartClickTime)/60 ;
double wait=([FrequencyPerHour doubleValue]+[PromptTime doubleValue]+2.75);
double warningTime=([FrequencyPerHour doubleValue]+[WarningTime doubleValue]);
if (WarninigBeep == NO) {
if ((Timeinterval*60) >= warningTime)
{
// NSString *path = [NSString stringWithFormat:@"%@/rapidbeep9.mp3", [[NSBundle mainBundle] resourcePath]];
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"rapidbeep9"
ofType:@"mp3"
inDirectory:nil];
NSURL *soundUrl = [NSURL fileURLWithPath:filePath];
// Create audio player object and initialize with URL to sound
_audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:soundUrl error:nil];
[_audioPlayer play];
WarninigBeep=YES;
}
}
[lbltimerDisplay setText:[NSString stringWithFormat:@"%.4f", Timeinterval]];}
这里有几个问题:
-
你在后台线程上运行计时器,但要更新 UI,这应该始终在主线程上进行。如果要在后台线程上运行此操作,请确保将 UI 更新调度回主线程:
dispatch_async(dispatch_get_main_queue(), ^{ self.timerLabel.text = [NSString stringWithFormat:@"%.4f", Timeinterval]; });
-
您正在此后台线程上启动运行循环,但永远不会停止它。如果您查看
run
的文档,它会警告您:如果希望运行循环终止,则不应使用此方法。相反,请使用其他运行方法之一,并在循环中检查您自己的其他任意条件。一个简单的例子是:
BOOL shouldKeepRunning = YES; // global NSRunLoop *theRL = [NSRunLoop currentRunLoop]; while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
多次重复此过程,则会消耗所有非常有限的全局队列工作线程,使用所有这些废弃的运行循环减慢应用的速度,并耗尽内存。
-
更简单的是,如果您希望计时器在后台线程上运行,则根本不要使用
NSTimer
/NSRunLoop
,而是使用可以在后台队列上运行而无需运行循环的GCD计时器。因此,为计时器声明一个属性:@property (nonatomic, strong) dispatch_source_t timer;
然后像这样启动和停止计时器:
- (void)startTimer { dispatch_queue_t queue = dispatch_queue_create("com.domain.app.timer.16782529", 0); self.timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue); if (self.timer) { dispatch_source_set_timer(self.timer, dispatch_walltime(NULL, 0), 0.1 * NSEC_PER_SEC, 0); dispatch_source_set_event_handler(self.timer, ^{ // do/call whatever you want here }); dispatch_resume(self.timer); } } - (void)cancelTimer { dispatch_source_cancel(self.timer); }
-
我认为您现在不应该解决这个问题,但是如果我想不断更新时间的显示,然后每秒播放一次声音,我会:
-
使用显示链接显示经过的时间。它就像一个计时器,但针对屏幕更新进行了优化。如果您要在屏幕上更新某些内容,这将提供最佳的用户体验。
-
为了播放声音(每秒一次?),我只需要使用一个标准的计时器。而且我不会倾向于每次都创建一个新
AVAudioPlayer
,而是这样做一次,然后以任何有意义的频率播放音频。
-
说了这么多,我隐约怀疑这里可能还有其他更深层次的问题,但似乎你应该首先解决这些计时器/运行循环问题,然后看看其他问题是否仍然存在。
此问题的标题假定某些内容由于内存压力而被释放。记忆压力不是这样表现出来的。更可能的问题是,当此后台进程仍在触发时,视图控制器已被解除分配。我建议在解除分配的例程中添加一个 NSLog
语句,看看崩溃(如果在解决上述问题后仍然发生)发生在释放之前还是之后。
无论如何,如果它仍然崩溃,请查看堆栈跟踪(或添加异常断点),以便您可以找到它崩溃的确切位置。在不知道问题出在哪里的情况下很难诊断。