Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

grandM

macrumors 68000
Original poster
Oct 14, 2013
1,508
298
Messing around with Timer. In deinit() I'm invalidating it. If I wouldn't do so, wouldn't the strong pointer be removed when I leave the VC?
 

AxoNeuron

macrumors 65816
Apr 22, 2012
1,251
855
The Left Coast
Messing around with Timer. In deinit() I'm invalidating it. If I wouldn't do so, wouldn't the strong pointer be removed when I leave the VC?
The problem is, the timer creates a strong reference to 'self'. So 'self' will never become deinitialized if it also has a strong reference to the timer. Remember, two objects that both have strong references to each other will never get deinitialized.

There's a few ways to solve this:

1. Don't call invalidate() on the timer in the deinit() method, because that won't get called UNTIL the timer has been invalidated. Instead, call deinit() somewhere earlier in the program, for a view controller you would call it in viewWillDisappear() (keep in mind you will need to re-initialize the timer in viewWillAppear() since the user may navigate back to this VC).

2. Pass a weak reference to 'self' when you initialize the timer. It would look like this:

Code:
weak let weakSelf = self;
var testTimer = Timer.scheduledTimer(timeInterval: 0.1, targret: weakSelf, selector: #selector(self.testTimerFired), userInfo: nil, repeats: true);

3. There are others but those are the two most common solutions I use. Memory management in Swift is still a pain, but it is still worlds better than pre-ARC Objective-C for sure. A piece of advice is to always put logging statements in your timers, at least at the start, to make sure they are getting called when you think they're getting called, and AREN'T getting called later on. This has saved my bacon many times when I was starting out in iOS development.
 
  • Like
Reactions: grandM

grandM

macrumors 68000
Original poster
Oct 14, 2013
1,508
298
The problem is, the timer creates a strong reference to 'self'. So 'self' will never become deinitialized if it also has a strong reference to the timer. Remember, two objects that both have strong references to each other will never get deinitialized.

There's a few ways to solve this:

1. Don't call invalidate() on the timer in the deinit() method, because that won't get called UNTIL the timer has been invalidated. Instead, call deinit() somewhere earlier in the program, for a view controller you would call it in viewWillDisappear() (keep in mind you will need to re-initialize the timer in viewWillAppear() since the user may navigate back to this VC).

2. Pass a weak reference to 'self' when you initialize the timer. It would look like this:

Code:
weak let weakSelf = self;
var testTimer = Timer.scheduledTimer(timeInterval: 0.1, targret: weakSelf, selector: #selector(self.testTimerFired), userInfo: nil, repeats: true);

3. There are others but those are the two most common solutions I use. Memory management in Swift is still a pain, but it is still worlds better than pre-ARC Objective-C for sure. A piece of advice is to always put logging statements in your timers, at least at the start, to make sure they are getting called when you think they're getting called, and AREN'T getting called later on. This has saved my bacon many times when I was starting out in iOS development.
Thanks. I was trying to use instruments to find the memory leak. Strangely enough leaks is showing no leaks when I'm removing the timer in deinit?
Concerning the retain cycle: the timer creates a strong reference to self in the initialization of the timer object, right? Is the second reference not just the timer property. Couldn't it be solved by declaring it as weak let timer: Timer?
The documentation states you ought to call for invalidate. But why isn't setting the object to nil sufficient?

In your second solution I reckon you can still invalidate in deinit?
 
Last edited:

AxoNeuron

macrumors 65816
Apr 22, 2012
1,251
855
The Left Coast
Thanks. I was trying to use instruments to find the memory leak. Strangely enough leaks is showing no leaks when I'm removing the timer in deinit?
Concerning the retain cycle: the timer creates a strong reference to self in the initialization of the timer object, right? Is the second reference not just the timer property. Couldn't it be solved by declaring it as weak let timer: Timer?
The documentation states you ought to call for invalidate. But why isn't setting the object to nil sufficient?

In your second solution I reckon you can still invalidate in deinit?
Yeah setting the timer as a weak variable should also solve the problem.
 

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
Something like viewWillDisappear() would seem like a safe place to invalidate the timer. Make your Timer ivar an optional, not weak. Using weak with Timers isn't going to help. A general rule is when you invalidate() always set the iVar to nil or to a new Timer. This will save you from debugging oddities with the Timer. The Timer is always either valid and running or invalid and nil.
 

grandM

macrumors 68000
Original poster
Oct 14, 2013
1,508
298
Something like viewWillDisappear() would seem like a safe place to invalidate the timer. Make your Timer ivar an optional, not weak. Using weak with Timers isn't going to help. A general rule is when you invalidate() always set the iVar to nil or to a new Timer. This will save you from debugging oddities with the Timer. The Timer is always either valid and running or invalid and nil.
This would implicate you'd have to set the Timer object to nil in viewWillDisAppear()? For the deinit() would still not be called if you'd set it to nil in deinit. Isn't setting it to nil then the same as calling the invalidate()?
Why wouldn't declaring the Timer object as a weak property help out? Isn't the retain cycle broken then?
 

PhoneyDeveloper

macrumors 68040
Sep 2, 2008
3,114
93
In viewWillDisappear() you should both invalidate and nil the Timer. Like I said, always do both, invalidate and nil.

While making the timer property weak will break the retain cycle you still have to invalidate() it. Invalidating in viewWillDisappear, or another method that's called when the ViewController is about to go away, makes most sense to me.
 

grandM

macrumors 68000
Original poster
Oct 14, 2013
1,508
298
In viewWillDisappear() you should both invalidate and nil the Timer. Like I said, always do both, invalidate and nil.

While making the timer property weak will break the retain cycle you still have to invalidate() it. Invalidating in viewWillDisappear, or another method that's called when the ViewController is about to go away, makes most sense to me.
With a clock I do not really feel the need to invalidate a Timer object upon disappearing. But I get your point it would stress the phone more when you keep it in memory. I was wondering what the best way was to break the retain cycle. As the property is default strong and the timer is creating a strong pointer to self in the scheduledTimer method I wondered if it wasn't simpler to declare the property as weak instead of creating a weak self and passing this weak self to the scheduledTimer method.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.