Can anyone explain me self.timer=nil
vs [self.timer invalidate]
?
What exactly happens at the memory location of self.timer
?
In my code
self.timer=nil
doesn't stops the timer but
[self.timer invalidate]
stops the timer.
If you require my code I will update that too.
Once you have no need to run timer, invalidate timer object, after that no need to nullify its reference.
This is what Apple documentation says: NSTimer
Once scheduled on a run loop, the timer fires at the specified
interval until it is invalidated. A non-repeating timer invalidates
itself immediately after it fires. However, for a repeating timer, you
must invalidate the timer object yourself by calling its invalidate
method. Calling this method requests the removal of the timer from the
current run loop; as a result, you should always call the invalidate
method from the same thread on which the timer was installed.
Invalidating the timer immediately disables it so that it no longer
affects the run loop. The run loop then removes the timer (and the
strong reference it had to the timer), either just before the
invalidate method returns or at some later point. Once invalidated,
timer objects cannot be reused.
First of all, invalidate
is a method of NSTimer
class which can use to stop currently running timer. Where when you assign nil
to any object then, in an ARC environment the variable will release the object.
Its important to stop running timer when you don't longer need, so we write [timer invalidate]
and then we write timer = nil;
to make sure it'll loose its address from memory and later time you can recreate the timer.
There is a key difference not mentioned in the other answers.
To test this drop the following code in Playground.
1st Attempt:
import Foundation
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
class Person{
var age = 0
lazy var timer: Timer? = {
let _timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(fireTimer), userInfo: nil, repeats: true)
return _timer
}()
init(age: Int) {
self.age = age
}
@objc func fireTimer(){
age += 1
print("age: \(age)")
}
deinit {
print("person was deallocated")
}
}
var person : Person? = Person(age: 0)
let _ = person?.timer
person = nil
So let me ask you a question. At the last line of the code I just set person
to nil
. That means the person
object is deallocated and all its properties are set to nil
and removed from memory. Right?
An object is deallocated as long as no other object is holding a strong a reference to it. In our case the timer
is still holding a strong reference to person, because the run-loop has a strong reference to the timer§ hence the person
object will not get deallocated.
The result of the above code is that it still continues to execute!
Let's fix it.
2nd Attempt:
Let's set the timer to nil
. This should remove the strong reference of timer
pointing to person
.
var person : Person? = Person(age: 0)
let _ = person?.timer
person?.timer = nil
person = nil
WRONG! We only removed our pointer to the timer
. Yet the result of the above code is just like our initial attempt. It still continues to execute...because the run loop is still keeping its pointer to it.
So what do we need to do?
Glad you asked. We must invalidate
the timer!
3rd Attempt:
var person : Person? = Person(age: 0)
let _ = person?.timer
person?.timer = nil
person?.timer?.invalidate()
person = nil
This looks better, but it's still wrong. Can you guess why?
I'll give you a hint. See code below