Im trying to execute the function pepe() repeated times, i get no errors but it doesnt work.
Here is my code:
public class MyClass {
var timer = Timer()
@objc func pepe() -> String {
let hola = "hola"
return hola
}
func startTimer(){
let seconds = 1.0
timer = Timer.scheduledTimer(timeInterval: seconds, target: ().self, selector: #selector(pepe), userInfo: nil, repeats: false)
}
func stopTimer() {
timer.invalidate()
}
init() {
self.startTimer()
self.stopTimer()
}
}
var pepe = MyClass()
pepe.stopTimer()
pepe.startTimer()
invalidate() Stops the timer from ever firing again and requests its removal from its run loop.
Pause/Resume Implementation Swift's Timer class has a method called invalidate(). It will stop the timer, but not reset the current value of seconds. This will be useful for resuming.
selector() is where you'd add in the function that you want it to call every timeInterval you set. In your example it's every second. Do bare in mind that in Swift 4 and above, you need to add @objc before a function if you want to call it in a selector like so: @objc func handleEverySecond() { print("Hello world!")
I would suggest:
Don't instantiate an empty Timer
. Consider:
var timer = Timer()
That is creating a blank timer instance. We don't want to do that. You should instead use:
weak var timer: Timer?
That achieves a few things:
The Timer?
syntax says it's an "optional", whose value you will instantiate later.
When the Timer
is scheduled, the runloop keeps a strong reference to it. So, unlike most other objects, you don't personally need to keep a strong reference to your scheduled timers. And you might want the timer
variable to be automatically set to nil
when the timer is invalidated. So, the weak
qualifier says that when the timer is invalidated, the timer
variable will automatically be set to nil
.
The pepe
method signature is not quite right:
It shouldn't return anything;
You should probably give it the Timer
parameter. It's a good habit to get into. You might not need it here, but it makes the intent of the method more clear and you may eventually find it useful to have that Timer
parameter; and
I might give it a more descriptive name to avoid any ambiguity; I tend to use names like timerHandler
.
There's no point in starting and stopping your timer in init
.
That reference to ().self
in the target
should just be self
.
In your playground, you're stopping the timer (that hasn't been scheduled started yet) and then starting it.
You might also want to stop it after a little time, so you get a chance to see the Timer
in action.
But, as general rule, when writing method to start the timer, it is prudent to make sure you hadn't (accidentally) already started it. If you don't do this, and accidentally call startTimer
twice, you can end up with multiple timers going at the same time (and worst, having lost references to the earlier timers). One common solution typical solution is to see if there is a already timer, and if so, invalidate it before you create you next timer. This is easily accomplished with the optional chaining pattern:
func startTimer() {
timer?.invalidate() // stops previous timer, if any
// now proceed with scheduling of new timer
}
Thus:
import UIKit
// if doing this in playground, include the following two lines
import PlaygroundSupport
PlaygroundPage.current.needsIndefiniteExecution = true
// MyClass
public class MyClass {
weak var timer: Timer?
@objc func timerHandler(_ timer: Timer) {
let hola = "hola"
print(">>>> \(hola)")
}
func startTimer() {
timer?.invalidate() // stops previous timer, if any
let seconds = 1.0
timer = Timer.scheduledTimer(timeInterval: seconds, target: self, selector: #selector(timerHandler(_:)), userInfo: nil, repeats: true)
}
func stopTimer() {
timer?.invalidate()
}
}
var object = MyClass()
object.startTimer()
// stop it 10 seconds later
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
object.stopTimer()
}
It should be recognized, though, that you can end up with something akin to a strong reference cycle:
target
;MyClass
(the target
) is what presumably is responsible for eventually invalidating that timer.As a result, MyClass
can't be deallocated until the Timer
is invalidated. And, as it stands, you cannot just invalidate
the Timer
in the deinit
of MyClass
, because deinit
won't get called until the the timer is invalidated.
The net effect is that if you have, for example, this MyClass
as a property of your view controller and start the timer and then dismiss the view controller, the timer will keep going and MyClass
won't be deallocated.
To solve this, you might want to use closure based timer with [weak self]
reference, eliminating the strong reference between the timer and MyClass
. You can then also automatically invalidate the timer when the MyClass
is deallocated:
public class MyClass {
weak var timer: Timer?
deinit {
timer?.invalidate()
}
func timerHandler(_ timer: Timer) {
let hola = "hola"
print(">>>> \(hola)")
}
func startTimer() {
timer?.invalidate() // stops previous timer, if any
let seconds = 1.0
timer = Timer.scheduledTimer(withTimeInterval: seconds, repeats: true) { [weak self] timer in
self?.timerHandler(timer)
}
}
func stopTimer() {
timer?.invalidate()
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With