【问题标题】:NSTimer memory managementNSTimer 内存管理
【发布时间】:2012-11-12 03:32:23
【问题描述】:

当我执行这段代码时:

[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(showButtons) userInfo:nil repeats:NO];

我需要 nil 还是释放它,或者用于内存管理?

我正在使用 ARC

【问题讨论】:

    标签: ios nstimer strong-references


    【解决方案1】:

    是的,NSTimer 将保持对target 的强引用,这可能会导致(尤其是在重复计时器中)强引用周期(也称为保留周期)。但是,在您的示例中,计时器不会重复,并且仅延迟 0.5,因此在最坏的情况下,您将拥有一个强大的参考周期,它将在 0.5 秒内自动解决。

    但未解决的强引用循环的一个常见示例是具有重复的NSTimer 属性的UIViewController,但由于NSTimer 具有对UIViewController 的强引用,控制器将结束被保留。

    因此,如果您将 NSTimer 保留为实例变量,那么,是的,您应该 invalidate 它来解决强引用循环。如果您只是调用scheduledTimerWithTimeInterval,但没有将其保存到实例变量中(可能从您的示例中推断出),那么当NSTimer 完成时,您的强引用循环将得到解决。

    顺便说一句,如果您正在处理重复的NSTimers,请不要尝试在dealloc 的所有者invalidateNSTimer,因为dealloc 显然不会调用直到解决强引用循环。以UIViewController 为例,您可以在viewDidDisappear 中执行此操作。

    顺便说一句,Advanced Memory Management Programming Guide 解释了什么是强引用循环。显然,这是在他们描述正确使用弱引用的部分中,这在此处不适用(因为您无法控制NSTimer 使用对目标的强引用这一事实),但它确实解释了强引用循环的概念很好。


    如果您不希望您的 NSTimer 保留对 self 的强引用,在 macOS 10.12 和 iOS 10 或更高版本中,您可以使用块再现,然后使用 weakSelf 模式:

    typeof(self) __weak weakSelf = self;
    [NSTimer scheduledTimerWithTimeInterval:0.5 repeats:false block:^(NSTimer * _Nonnull timer) {
        [weakSelf showButtons];
    }];
    

    顺便说一句,我注意到你打电话给showButtons。如果您只想在视图上显示一些控件,则可以完全取消使用 NSTimer 并执行以下操作:

    self.button1.alpha = 0.0;
    self.button2.alpha = 0.0;
    
    [UIView animateWithDuration:0.25
                          delay:0.5
                        options:UIViewAnimationOptionCurveEaseInOut | UIViewAnimationOptionAllowUserInteraction
                     animations:^{
                         self.button1.alpha = 1.0;
                         self.button2.alpha = 1.0;
                     }
                     completion:nil];
    

    这不会遇到NSTimer 对象的保留问题,并且在一个语句中执行延迟以及按钮的优雅显示。如果你在 showButtons 方法中做额外的处理,你可以把它放在 completion 块中。

    【讨论】:

    • 很好的答案,但也许我没有明确说明我实际上并没有在 .h 文件中使用 NSTimer* 计时器将其删除,我只是在 .m 文件中添加了该代码,所以我不能使其无效或为零。在这种情况下,如果我使用我的计时器仍然可以,还是使用您提供给我的代码更好?
    • @Alessandro 我实际上假设您没有为计时器维护 ivar。但是,很明显,如果你想在viewWillDisappear 中输入invalidate,你就必须这样做。 (顺便说一句,您不必将私有 ivars 放在您的 .h 中;私有类扩展更好。)您曾多次提到将 NSTimer 设置为 nil。请注意,这并不能解决强引用循环。计时器要么需要完成(而不是重复),要么你需要invalidate(使用你的新ivar)。
    • 我完全理解我需要使计时器无效才能将其从运行循环中删除。这样做之后,就重新分配该属性而言,是否需要将计时器设置为 nil 并执行 if timer != nil 或者这是不必要的,只需执行 !timer.isValid 就足够了吗?换句话说,timer = nil 有什么用处吗?在您之前的评论中,您已经说过它与内存管理无关,所以我想知道为什么很多开发人员继续这样做,而 isValid 提高了清晰度......
    【解决方案2】:

    如果您将其保存在属性中,那么是的,您确实需要在触发选择器后将其设置为 nil。

    保存它也是安全的,以防您的班级因任何原因被释放,以便您可以在需要时[timer invalidate]

    【讨论】:

    • 如果您将该属性设置为weak,则您不必自己设置nil。它会在触发后自动释放(假设它是非重复的)。
    • @Rob 这样说是否正确:如果重复,则必须调用invalidate。否则,run-loop 将保留其指向 viewController 的指针,即 weakly 指向计时器
    • 或者,也许更准确地说,运行循环将保持对重复的定时器的强引用。如果定时器弱引用视图控制器,内存影响将是适度的,但不再需要的计时器将继续触发。
    【解决方案3】:

    是的,您可以使用:myTimer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(showButtons) userInfo:nil repeats:NO]; 然后在你看来DidDisappear [myTimer invalidate]

    【讨论】:

      【解决方案4】:

      使用使用闭包的 Timer 的新方法

      timer = Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true, block: {[weak self] (timer) in
         print("Tick tock")
         guard let ws = self else { return }
         //some action that repeats
         ws.myViewControllerMethod()
      })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-21
        • 2012-03-21
        相关资源
        最近更新 更多