14

When I execute this code:

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

do I need to nil it or release it, ot whatever for memory management?

I am using ARC

Rob
  • 415,655
  • 72
  • 787
  • 1,044
Alessandro
  • 4,000
  • 12
  • 63
  • 131

4 Answers4

36

Yes, NSTimer will maintain a strong reference to the target, which can cause (especially in repeating timers) strong reference cycles (a.k.a. retain cycles). In your example, though, the timer does not repeat, and is delayed only 0.5, so worst case scenario, you will have a strong reference cycle that will automatically resolve itself in 0.5 seconds.

But a common example of an unresolved strong reference cycle would be to have a UIViewController with a NSTimer property that repeats, but because the NSTimer has a strong reference to the UIViewController, the controller will end up being retained.

So, if you're keeping the NSTimer as an instance variable, then, yes, you should invalidate it, to resolve the strong reference cycle. If you're just calling the scheduledTimerWithTimeInterval, but not saving it to an instance variable (as one might infer from your example), then your strong reference cycle will be resolved when the NSTimer is complete.

And, by the way, if you're dealing with repeating NSTimers, don't try to invalidate them in dealloc of the owner of the NSTimer because the dealloc obviously will not be called until the strong reference cycle is resolved. In the case of a UIViewController, for example, you might do it in viewDidDisappear.

By the way, the Advanced Memory Management Programming Guide explains what strong reference cycles are. Clearly, this is in a section where they're describing the proper use of weak references, which isn't applicable here (because you have no control over the fact that NSTimer uses strong references to the target), but it does explain the concepts of strong reference cycles nicely.


If you don't want your NSTimer to keep a strong reference to self, in macOS 10.12 and iOS 10, or later, you can use the block rendition and then use the weakSelf pattern:

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

By the way, I notice that you're calling showButtons. If you're trying to just show some controls on your view, you could eliminate the use of the NSTimer altogether and do something like:

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];

This doesn't suffer the retain issues of NSTimer objects, and performs both the delay as well as the graceful showing of the button(s) all in one statement. If you're doing additional processing in your showButtons method, you can put that in the completion block.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Great answer, but maybe i didn't make clear the fact that I don't actually declere it in the .h file with NSTimer* timer, I just add that code in the .m file, so I cannot invalidate or nil it. In this case is it still ok if I use my timer, or is it better to use the code you provided me? – Alessandro Nov 23 '12 at 22:41
  • @Alessandro I actually presumed you didn't maintain an ivar for the timer. But, obviously, if you want to `invalidate` in `viewWillDisappear`, you'll have to do so. (As an aside, you don't have to put private ivars in your .h; private class extension is better.) And a couple of times you've mentioned about setting a `NSTimer` to `nil`. Please note that this does not resolve strong reference cycle. The timer either needs to either complete (and not be repeating) or you need to `invalidate` (using your new ivar). – Rob Nov 23 '12 at 22:54
  • I fully understand that I need to invalidate a timer for it to be removed from the run-loop. After doing such, in terms of re-assigning that property is there any need to set the timer to `nil` and do a `if timer != nil` or that's unnecessary and just doing a `!timer.isValid` is enough? in other words is there any usage to do `timer = nil`? In your previous comment you've already said it has nothing to with memory management so I wonder why a lot of devs keep doing that while `isValid` increases clarity... – mfaani Nov 29 '18 at 18:28
2

If you are saving it in a property, then yes, you do need to set it to nil after it fired the selector.

It's also safe to save it in case your class gets deallocated for whatever reason, so that you can [timer invalidate] if you need to.

Ismael
  • 3,927
  • 3
  • 15
  • 23
  • If you make that property `weak`, you don't have to `nil` it yourself. It will automatically release itself after it fires (assuming it was non-repeating). – Rob Mar 01 '17 at 21:14
  • @Rob Would it be correct to say that: if it was repeating then calling `invalidate` becomes mandatory. Otherwise the run-loop will retain its pointer towards the viewController which is `weak`ly pointing to the timer – mfaani Oct 29 '18 at 15:48
  • Or, perhaps more accurately, the run loop will keep a strong reference to the repeating _timer._ If the timer is weakly referencing the view controller, the memory impact will be modest, but a timer that is no longer needed will keep firing. – Rob Oct 30 '18 at 14:11
1

Yes, you can use: myTimer=[NSTimer scheduledTimerWithTimeInterval:0.5 target:self selector:@selector(showButtons) userInfo:nil repeats:NO]; And then in your viewDidDisappear [myTimer invalidate]

Kerberos
  • 4,036
  • 3
  • 36
  • 55
1

Use the new method of Timer that uses closures

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()
})