5

以下是我的代码:

.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 成员变量的正确方法是什么。

4

5 回答 5

16

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

每当我们将self目标分配给 时NSTimer,计时器都会保存对我们对象的引用。如果计时器正在重复(或有很长的时间段),它不会自行失效(或者如果不重复,则自动失效需要很长时间)。因此,即使对象同时被释放,它也不会调用该dealloc方法,因为保留计数至少为 1。现在,我试图通过调用重复释放对象来强制释放我的对象,直到保留计数变成了 0。那是我的错误。

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

为了解决这个问题,首先我清理了我的行为并删除了强制解除对象的代码。然后就在我想要释放对象之前,我调用了 NSTimer 的 invalidate 函数。这释放了 Timer 拥有的目标实例。之后,调用release我的对象成功地解除了它。

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

于 2009-12-10T18:41:17.327 回答
5

您应该解决一些问题来清理它。

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

@interface GObject : NSObject

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

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

- (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;
于 2009-12-09T19:31:55.880 回答
2

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

于 2011-11-02T04:20:19.910 回答
0

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

于 2011-01-09T10:35:48.080 回答
0

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

现在计时器不会保留对self和何时self被释放的引用,- dealloc应该调用并且您可以使TimerController计时器本身无效并将其设置为 nil。

于 2014-03-05T13:50:15.847 回答