在 dealloc 中使 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 成员变量的正确方法是什么。
Following is my code:
.h file:
#import "Foundation/Foundation.h"
@interface GObject:NSObject{
NSTimer* m_Timer;
}
@property(nonatomic, retain) NSTimer* m_Timer;
- (void)Initialize;
- (void)TimerCallback:(NSTimer*)pTimer;
@end
.m file:
@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
Now when the dealloc gets called, the program crashes in the line invalidating the timer. The next two lines don't even get called. I get a "EXC_BAD_ACCESS" error. Can anyone tell me why that might be happening, and what is the proper way of stopping and releasing a NSTimer member variable in a class.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我做了一些研究和测试,并且可以找到我自己问题的答案。好的,事情就是这样:
每当我们将 self 指定为 NSTimer 的目标时,计时器就会保存对我们对象的引用。如果计时器正在重复(或具有很长的时间段),则它不会自行失效(或者如果不重复,则需要很长时间才能自动失效)。因此,即使对象同时被释放,它也不会调用dealloc方法,因为保留计数至少为1。现在,我强制尝试通过调用来释放我的对象重复释放它直到保留计数变成0。这是我的错误。
但如果你不这样做,你的对象将保持活动状态,并且最终会出现内存泄漏,因为你通过各种释放丢失了对该对象的其余引用。唯一保留的将是 NSTimer。这听起来像是一个僵局。我的代码崩溃了,因为当
delloc
试图使NSTimer
无效时,它试图释放它所持有的引用。但由于我很聪明,已经将保留计数减少到 0,这会导致内存异常。为了解决这个问题,首先我清理了我的行为并删除了强制释放对象的代码。然后就在我想要释放对象之前,我调用了 NSTimer 的 invalidate 函数。这释放了计时器拥有的目标实例。之后,在我的对象上调用
release
成功地解除了它的分配。最重要的是,如果您的对象具有重复或不会自动失效(克服)的 NSTimers,切勿在 delloc 函数中使它们失效。在计时器将实例保存到对象之前,delloc 不会被调用。相反,在释放对象之前,有一个清理函数来使计时器无效。这就是我想出的解决方案。如果有更好的,我当然想知道。
I did some research and tests, and could figure out the answer to my own question. Ok, here it goes:
Whenever we assign
self
as target to theNSTimer
, the timer holds a reference to our object. If the timer is repeating (or has a long time period), it would not get invalidated on its own (or will take too long to invalidate automatically, if not repeating). So, even if the object is released in the meantime, it wouldn't call thedealloc
method, as the retain count would at least be 1. Now, I was forcibly trying to dellocate my object by calling repeated releases on it till the retain count became 0. That was my mistake.But if you do not do that, your object will keep alive, and you will eventually have a memory leak as you lose the rest of the references to that object by various releases. The only one kept will be with the
NSTimer
. This sounds like a deadlock situation. My code crashed, because when thedelloc
tried to invalidate theNSTimer
, it tried to release the reference that it was holding. But since I had been a smartass and reduced the retain count to 0 already, that would cause a memory exception.To solve this, firstly I cleaned up my act and removed the code to forcibly dellocate the object. Then just before I wanted the object to be dellocated, I called the NSTimer's invalidate function. This released the target instance that the Timer had. After that, calling
release
on my object successfully dellocated it.The bottom line is, if your object has NSTimers that repeat or do not invalidate (get over) automatically, never invalidate them in the delloc function. The delloc will not get called till the timer is holding the instance to your object. Instead have a cleanup function to invalidate the timers, before releasing the object. That is the solution that I came up with. If there is a better one out there, I most certainly want to know.
您应该解决一些问题来清理这个问题。
你的班级声明有点不对劲。如果你想继承 NSObject,正确的语法是:
在你的实现中,你应该实现
- (id)init
而不是- (void)Initialize
。没有实例方法- (void)Initialize
...有一个静态方法+ (void)initialize
。请注意+
和大小写的差异,这很重要):在类接收其第一个方法之前,在程序中调用一次initialize
方法。在这种情况下,您的
Initialize
方法根本没有被调用(它拼写错误,并且它是一个实例方法而不是静态方法)。相反,您想要实现init
,它是 NSObject 实例的指定初始值设定项:最后,请务必在属性语句之前使用
@
符号:并且不要忘记在您的实现中综合它:
There are a few things you should address to clean this up.
Your class declaration is a little off. If you want to inherit from NSObject, the correct syntax is:
In your implementation, you should implement
- (id)init
rather than- (void)Initialize
. There is no instance method- (void)Initialize
... there is a static method+ (void)initialize
. Note the+
and the difference in capitalization, which is significant): theinitialize
method is called once in your program before the class receives its first method.In this case, your
Initialize
method is not being called at all (it is spelled wrong, and it is an instance method instead of a static method). Instead, you want to implementinit
, which is the designated initializer for NSObject instances:Lastly, be sure to use the
@
symbol before the property statement:And don't forget to synthesize it in your implementation:
好吧,我不得不说,你可以参考苹果开发者的文档,比如苹果的类参考。从那里可以看到,当调用invalidate:方法时,定时器的release:方法将在invalidate:方法返回之前被调用。
well, i have to say that you can refer to apple developer's documentation, such as Apple's class reference. You can see from there that when invalidate: method is called, the timer's release: method will be called before invalidate: method returns.
我在其中一个视图控制器上重复使用 NSTimer 时遇到了相同的内存泄漏问题。该视图控制器在应该被释放的时候却不会被释放。我最终通过将计时器移至主应用程序委托代码来解决此问题,无需释放计时器。
I have the same memory leak issue by using repeated NSTimer on one of the view controller. This view controller will not get released when it should. I finally solved this issue by moving my timer to the main application delegate code where there is no need to release the timer.
另一种解决方案是创建一个单独的对象来处理 NSTimer 回调(某些 TimerController)。如果需要,该对象可以调用您的 self 控制器上的方法。
现在计时器不会保留对
self
的引用,并且当self
被释放时,应该调用- dealloc
并且您可以使其无效并设置为nilTimerController
和计时器本身。Another solution would be to create a separate object that deals with the
NSTimer
callback (someTimerController
). That object, if needed, can call the methods on yourself
controller.Now the timer won't retain a reference to
self
and whenself
gets deallocated,- dealloc
should be called and you can invalidate and set to nil theTimerController
and the timer itself.