释放NSTimer的正确方法?

发布于 2024-09-27 19:54:15 字数 443 浏览 3 评论 0原文

在我的 dealloc 方法中释放 NSTimer 的正确方法是什么?它是用以下代码创建的?

-(void)mainTimerLoop {

     mainTimer = [NSTimer scheduledTimerWithTimeInterval:1/10
                                                  target:self 
                                                selector:@selector(gameLoop) 
                                                userInfo:nil
                                                 repeats:YES];
}

谢谢

What is the correct way to release a NSTimer in my dealloc method ? It was created with the following code ?

-(void)mainTimerLoop {

     mainTimer = [NSTimer scheduledTimerWithTimeInterval:1/10
                                                  target:self 
                                                selector:@selector(gameLoop) 
                                                userInfo:nil
                                                 repeats:YES];
}

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

温柔一刀 2024-10-04 19:54:15

按照你这样做的方式,你永远不会点击dealloc。计时器保留其目标。在这种情况下,这意味着计时器已保留您。在其失效之前,它不会释放您。由于您创建了计时器,因此您还必须在dealloc 之前的某个时刻将其无效,因为计时器的保留将阻止您的对象被dealloc 处理。

您有两个选择:

  • 找到另一个地方使计时器无效(视图离开屏幕,应用程序正在终止,您有什么)
  • 将其他内容设置为计时器的目标。

作为后者的一个例子:

@interface GameLoopTimerTarget : NSObject {
    id owner;  /* not retained! */
}
- (id)initWithOwner:(id)owner;
- (void)timerDidFire:(NSTimer *)t;
@end

@implementation GameLoopTimerTarget
- (id)initWithOwner:(id)owner_ {
    self = [super init];
    if (!self) return nil;

    owner = owner_;
    return self;
}

- (void)timerDidFire:(NSTimer *)t {
#pragma unused (t)
    [owner performSelector:@selector(gameLoop)];
}
@end

/* In your main object… */
/* assume synthesized:
@property (retain, NS_NONATOMIC_IPHONE_ONLY) GameLoopTimer *mainTimerTarget; */
- (void)mainTimerLoop {
    self.mainTimerTarget = [[[GameLoopTimerTarget alloc] initWithOwner:self] autorelease];
    mainTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0 target:self.mainTimerTarget selector:@selector(timerDidFire:) userInfo:nil repeats:YES];
}

- (void)dealloc {
    /* other stuff */
    [timer invalidate], timer = nil;
    [mainTimerTarget release], mainTimerTarget = nil;
    /* more stuff */
    [super dealloc];
}

注意时间间隔是 1.0/10.0 - 这也可以写成 0.1,但它不能写成1/10,因为该除法将截断为 0.0

另请注意这如何打破保留周期:

  • 您和您的计时器都保留计时器目标。
  • 您在正常时间点击了dealloc。
  • 然后,您使计时器无效并释放计时器目标。
  • 然后计时器目标被释放。

The way you're doing it, you won't ever hit dealloc. A timer retains its target. In this case, that means the timer has retained you. It will not release you until it is invalidated. Since you created the timer, you must also invalidate it at some point prior to dealloc, because the timer's retain will prevent your object's being dealloced.

You have two options:

  • find another place to invalidate the timer (view goes offscreen, application is terminating, what have you)
  • set something else as the timer's target.

As an example of the latter:

@interface GameLoopTimerTarget : NSObject {
    id owner;  /* not retained! */
}
- (id)initWithOwner:(id)owner;
- (void)timerDidFire:(NSTimer *)t;
@end

@implementation GameLoopTimerTarget
- (id)initWithOwner:(id)owner_ {
    self = [super init];
    if (!self) return nil;

    owner = owner_;
    return self;
}

- (void)timerDidFire:(NSTimer *)t {
#pragma unused (t)
    [owner performSelector:@selector(gameLoop)];
}
@end

/* In your main object… */
/* assume synthesized:
@property (retain, NS_NONATOMIC_IPHONE_ONLY) GameLoopTimer *mainTimerTarget; */
- (void)mainTimerLoop {
    self.mainTimerTarget = [[[GameLoopTimerTarget alloc] initWithOwner:self] autorelease];
    mainTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/10.0 target:self.mainTimerTarget selector:@selector(timerDidFire:) userInfo:nil repeats:YES];
}

- (void)dealloc {
    /* other stuff */
    [timer invalidate], timer = nil;
    [mainTimerTarget release], mainTimerTarget = nil;
    /* more stuff */
    [super dealloc];
}

Notice how the time interval is 1.0/10.0 - this could also be written 0.1, but it cannot be written 1/10, as that division will truncate to 0.0.

Also notice how this breaks the retain cycle:

  • Both you and your timer retain the timer target.
  • You hit dealloc at the normal time.
  • You then invalidate the timer and release the timer target.
  • The timer target is then deallocated.
生死何惧 2024-10-04 19:54:15

有效的 NSTimer 由运行循环保留,如果它重复,它将永远保留或直到您使其无效为止。您不应该释放它,因为在您的示例代码中,您没有显式保留它。如果您使其无效,它将不再被运行循环保留,并且将被自动释放。

这对于重复计时器来说可能没问题,但对于一次性计时器来说很危险,因为在您访问它以查看它是否有效和/或尝试使其无效之前,它可能会被释放(这会导致不好的-访问应用程序崩溃)。因此,如果您计划以任何方式在创建计时器变量后查看它(包括检查它、使其无效和/或释放它),则最好将其显式保留在应用程序中的某个位置,然后无效后释放它并设置为 nil 就完成了。

如果将其声明为保留属性,则可以在一条语句中释放它并将其设置为 nil。然后你可以写:

self.timer = nil;

A valid NSTimer is retained by the run loop, which, if it is repeating, will be forever or until you invalidate it. You shouldn't release it, since, in your example code, you did not explicitly retain it. If you invalidate it, it will no longer be retained by the run loop, and will be autoreleased.

This might be OK for a repeating timer, but is dangerous for a one-shot timer, since it might end being released before you ever access it to see if it's valid and/or try to invalidate it (which would lead to a bad-access app crash). Therefore if you plan on, in any way, looking at a timer variable after it's creation (including to check it, invalidate it and/or release it), it might be a good practice to explicitly retain it somewhere in your app, and then release it and set it to nil after it's invalid and you are done with it.

You can release it and set it to nil in one statement if you declare it as a retain property. Then you can write:

self.timer = nil;
慕巷 2024-10-04 19:54:15

你在这里有一个关于 NSTimer 的非常好的答案 我如何使用 NSTimer? 他们在那里谈论关于停止重复的 NSTimer 行为

[myTimer invalidate];

you have a really good answer about NSTimer here How do I use NSTimer? there they talk about stoping a repeating NSTimer doing

[myTimer invalidate];
你又不是我 2024-10-04 19:54:15

我认为这里最好的建议是 -

不要保留 NSTimer 实例,也不要释放它。

一旦它被调度到 NSRunloop(OP 示例中的当前运行循环)上,NSTimer 就会被运行循环保留,直到失效或直到运行循环停止

。应该做的,是在正确的时间使你的计时器无效 - 并且在你创建和调度它的同一线程上,

还要记住, NSTimer 保留其目标,并且不会让目标在它之前“死亡”。设计你的代码,这样你就没有一个保留周期,它会阻止你的对象(持有计时器)和计时器(持有你的对象)的释放。

I think the best advice here is -

Do not retain the NSTimer instance, and do not release it.

As soon as it is scheduled on an NSRunloop (current runloop in the OP's example, an NSTimer is retained by the runloop until being invalidated, or until the runloop stops.

What you should be doing, is to invalidate your timer at the right time - and on the same thread where you created and scheduled it.

Keep in mind, also, that NSTimer retains its target, and won't let the target "die" before it dies itself. design your code so that you don't have a retain cycle that will prevent the releasing of both your object (holding the timer) and the timer (holding you object).

新雨望断虹 2024-10-04 19:54:15

您不需要释放它,因为它会自动释放。任何由便捷方法创建的内容(即您自己不调用 alloc)都是被调用函数负责内存管理,这通常意味着它将调用 autorelease它在返回之前创建的对象。

我会使用 retain 关键字将计时器分配给一个属性,以确保它不会被释放给您。通常,如果自动释放的对象没有任何保留,则会在事件循环中释放它们。

You don't need to release it because it will be autoreleased. Anything created by a convenience method (i.e. you don't call alloc yourself) is the responsibility of the called function to memory manage, which usually means that it will call autorelease on the object it creates before it returns it.

I would assign the timer to a property with the retain keyword though to make sure it doesn't get deallocated on you. Generally autoreleased objects are deallocated in the event loop if they don't have any retains.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文