自定义定时器泄漏问题

时间:2014-03-25 00:31:59

标签: objective-c memory-leaks nstimer cpu-usage

在为iOS设计应用程序时,我们需要一个可以轻松暂停和恢复的计时器。因此我们创建了自己的计时器。但是,它似乎泄漏,导致整个应用程序中的CPU使用率增加。下面是计时器的全部代码。任何帮助将不胜感激。此外,我们正在使用ARC,而且我们对这个概念不熟悉,因此我们可能会在某处导致保留周期。

@implementation PausibleTimer
{
    BOOL hasPausedThisCycle;
}

+ (PausibleTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats
{
    PausibleTimer *newTimer = [[PausibleTimer alloc] init];
    newTimer.timeInterval = timeInterval;
    newTimer.target = target;
    newTimer.selector = selector;
    newTimer.userInfo = userInfo;
    newTimer.repeats = repeats;

    return newTimer;
}

- (void)start
{
    [self.timer invalidate]; // Invalidate current timer

    // Initialize an NSTimer with the PausibleTimer conditions
    self.timer = [NSTimer scheduledTimerWithTimeInterval:self.timeInterval target:self selector:@selector(timerFired:) userInfo:self.userInfo repeats:self.repeats];

    self.isPaused = NO; // Set isPaused to NO
}

- (void)pause
{
    // If the timer is currently paused, return
    if (self.isPaused)
    {
        return;
    }

    self.isPaused = YES; // Set isPaused to YES
    hasPausedThisCycle = YES; // Set hasPausedThisCycle to YES

    [self.timer invalidate]; // Invalidate current timer
}

- (void)timerFired:(NSTimer *)timer
{
    // If the timer is currently paused, return
    if (self.isPaused)
    {
        return;
    }

    // Ignore performSelector leaks
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
    if (self.selector)
    {
        [self.target performSelector:self.selector withObject:self];
    }
    else if (!self.selector)
    {
        [self invalidate];
    }
#pragma clang diagnostic pop

    if (hasPausedThisCycle)
    {
        hasPausedThisCycle = NO; // Set hasPausedThisCycle to NO

        if (self.repeats)
        {
            // Set up a new NSTimer with original timeInterval
            [self.timer invalidate];
            [self start];
        }
    }
}

- (void)invalidate
{
    [self.timer invalidate];
    self.timer = nil;
    self.selector = nil;
    self.target = nil;
    self.userInfo = nil;
}

@end

更新

这是头文件,显示了变量的声明。我从一开始就把目标设定为弱。从我使用乐器收集的内容,特别是时间分析器,它指向我的PausibleTimer并深入挖掘带我到mk_timer_arm的东西,据说我杀了我能做的一切之后仍然在运行。我在viewWillDisappear中使我的所有计时器无效并将其设置为nil。无论如何,这里是标题,我真的很感谢你的帮助。

#import <Foundation/Foundation.h>

@interface PausibleTimer : NSObject

// Set up NSTimer properties
@property (nonatomic) NSTimeInterval timeInterval;
@property (nonatomic, weak) id target;
@property (nonatomic) SEL selector;
@property (nonatomic) id userInfo;
@property (nonatomic) BOOL repeats;

@property (strong, nonatomic) NSTimer *timer; // Pointer to current timer
@property (nonatomic) BOOL isPaused; // Check if timer is paused

// PausibleTimer initializer
+ (PausibleTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval target:(id)target selector:(SEL)selector userInfo:(id)userInfo repeats:(BOOL)repeats;

- (void)pause;
- (void)start;
- (void)invalidate;

@end

1 个答案:

答案 0 :(得分:1)

您没有解释什么是泄漏,因此我将从您的代码中提出两个可能的考虑因素:

self.target

您没有显示实例变量的属性声明,但如果未明确声明self.target weak,那么您有一个保留周期和泄漏。假设我们想象一个具有PausibleTimer实例变量的视图控制器或类似对象。然后视图控制器保留PausibleTimer。但是PausibleTimer将视图控制器保留为target。 Presto,保持周期,泄漏。

self.timer

一般来说,ARC下的NSTimer存在很大问题。在无效之前,计时器会保留其目标。在这种情况下,NSTimer的目标是PausibleTimer实例。因此,如果PausibleTimer在拥有对象(视图控制器或其他任何东西)不存在时具有NSTimer(self.timer),即使ARC仍按顺序将release发送到PausibleTimer,不幸的是,NSTimer在PausibleTimer上仍有retain

解决方案

如果第二种情况属实,则您有一个保留周期。如果第一个也是真的,那么你有一个双重保留周期。

您无法通过在PausibleTimer中实施dealloc来解决此问题,从而使NSTimer无效,因为dealloc不会被调用;这就是整个问题。同样,您无法通过在视图控制器中实现dealloc来使PausibleTimer无效来解决此问题,因为(再次)dealloc不会被调用;这是(再次)整个问题。

基本上你已经采用了NSTimer已经存在的内存管理问题并将其推回了一个阶段,其方式可能会加剧它。

我使用视图控制器拥有的普通NSTimer来解决这个问题的方法是,在视图控制器不存在之前我明确地invalidate定时器,例如在viewWillDisappear:。这导致NSTimer释放其目标,该目标稍后可能会以良好的顺序存在。你可能必须做那样的事情。