目标c - 由于内存泄漏,标签被释放,我该怎么办



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 语句,看看崩溃(如果在解决上述问题后仍然发生)发生在释放之前还是之后。

无论如何,如果它仍然崩溃,请查看堆栈跟踪(或添加异常断点),以便您可以找到它崩溃的确切位置。在不知道问题出在哪里的情况下很难诊断。

最新更新