在dealloc中使NSTimer无效的问题

时间:2009-12-09 19:19:53

标签: iphone objective-c nstimer

以下是我的代码:

.h文件:

#import "Foundation/Foundation.h"
@interface GObject:NSObject{
    NSTimer* m_Timer;
}
@property(nonatomic, retain) NSTimer* m_Timer;

- (void)Initialize;
- (void)TimerCallback:(NSTimer*)pTimer;
@end

.m文件:

@implementation GObject

@synthesize m_Timer

- (void) Initialize{
    self.m_Timer = [NSTimer scheduledTimerWithTimeInterval:5.0 
                       target:self 
                       selector: @selector(TimerCallback:) 
                       userInfo: nil 
                       repeats: YES];

}

- (void)TimerCallback:(NSTimer*)pTimer {
    //Some Code
}
- (void)dealloc {
    [m_Timer invalidate]; //--Crashes Here
    [m_Timer release];
    m_Timer = nil;
    [super dealloc];
}
@end

现在,当调用dealloc时,程序在使计时器无效的行中崩溃。接下来的两行甚至都没有被调用。我收到“EXC_BAD_ACCESS”错误。任何人都可以告诉我为什么会发生这种情况,以及在类中停止和释放NSTimer成员变量的正确方法是什么。

5 个答案:

答案 0 :(得分:16)

我做了一些研究和测试,可以找出我自己的问题的答案。好的,这就是:

每当我们将self指定为NSTimer的目标时,计时器就会保存对我们对象的引用。如果计时器正在重复(或具有很长的时间段),它将不会自行失效(或者如果不重复则自动失效将花费太长时间)。因此,即使对象在此期间被释放,它也不会调用dealloc方法,因为保留计数至少为1.现在,我强行尝试通过调用重复发布来解除对象的释放直到保留计数变为0.这是我的错误。

但是如果你不这样做,你的对象将保持活跃状态​​,并且当你通过各种版本丢失对该对象的其余引用时,你最终会有内存泄漏。保留的唯一一个将是NSTimer。这听起来像是僵局。我的代码崩溃了,因为当delloc试图使NSTimer无效时,它会尝试释放它所持有的引用。但是因为我已经成为smartass并且已经将保留计数减少到0,这将导致内存异常。

为了解决这个问题,首先我清理了我的行为并删除了代码以强制解除对象的攻击。然后就在我想要取消定位对象之前,我调用了NSTimer的无效函数。这释放了Timer具有的目标实例。之后,在我的对象上调用release成功解除了它。

底线是,如果你的对象有NSTimers重复或不自动失效(克服),永远不会在delloc函数中使它们失效。在计时器将实例保存到您的对象之前,不会调用delloc。而是在释放对象之前具有清除功能以使计时器无效。这就是我提出的解决方案。如果那里有一个更好的,我当然想知道。

答案 1 :(得分:5)

有一些事情你应该解决这个问题。

你的班级声明有点偏。如果要从NSObject继承,正确的语法是:

@interface GObject : NSObject

在您的实施中,您应该实施- (id)init而不是- (void)Initialize。没有实例方法- (void)Initialize ...有一个静态方法+ (void)initialize。请注意+和大小写的差异(这很重要):在类接收第一个方法之前,在程序中调用initialize方法一次。

在这种情况下,您的Initialize方法根本没有被调用(拼写错误,它是一个实例方法而不是静态方法)。相反,您希望实现init,它是NSObject实例的指定初始化程序:

- (id)init {
    if (self = [super init]) {
        self.m_Timer = [NSTimer scheduledTimerWithTimeInterval:5.0 
                   target:self 
                   selector: @selector(TimerCallback:) 
                   userInfo: nil 
                   repeats: YES];
    }
    return self;
}

最后,请务必在属性声明前使用@符号:

@property(nonatomic, retain) NSTimer* m_Timer;

不要忘记在实施中综合它:

@implementation GObject

@synthesize m_Timer;

答案 2 :(得分:2)

好吧,我不得不说你可以参考苹果开发者的文档,比如Apple的类引用。从那里可以看到,当调用invalidate:方法时,将在invalidate:method返回之前调用timer的release:方法。

答案 3 :(得分:0)

通过在其中一个视图控制器上使用重复的NSTimer,我遇到了相同的内存泄漏问题。该视图控制器不会在应该发布时释放。我终于通过将我的计时器移动到主应用程序委托代码来解决了这个问题,而不需要释放计时器。

答案 4 :(得分:0)

另一个解决方案是创建一个单独的对象来处理NSTimer回调(某些TimerController)。如果需要,该对象可以调用self控制器上的方法。

现在,计时器不会保留对self的引用,当self被取消分配时,应调用- dealloc,您可以使TimerController无效并设置为nil {0}和计时器本身。