问题
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()
回答1:
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
Timeris 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 thetimervariable to be automatically set tonilwhen the timer is invalidated. So, theweakqualifier says that when the timer is invalidated, thetimervariable will automatically be set tonil.
The
pepemethod signature is not quite right:It shouldn't return anything;
You should probably give it the
Timerparameter. 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 thatTimerparameter; andI 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
().selfin thetargetshould just beself.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
Timerin 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
startTimertwice, 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:
- The runloop keeps a strong reference to scheduled timers;
- The selector-based timer keeps strong reference to its
target; - The
MyClass(thetarget) 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()
}
}
回答2:
Set repeats = true , make it self not ().self , I tried this and it works , also you have a mistake of trying to start and stop the timer in init
public class MyClass {
var timer = Timer()
@objc func pepe() {
let hola = "hola"
print("\(hola)")
}
func startTimer(){
let seconds = 1.0
timer = Timer.scheduledTimer(timeInterval: seconds, target:self, selector: #selector(pepe), userInfo: nil, repeats: true)
}
func stopTimer() {
timer.invalidate()
}
init() {
}
}
回答3:
try this
import UIKit
import AVFoundation
class PeopleViewController: UIViewController {
var countdownTimer: Timer!
var totalTime = 0
let timerLbl: UILabel = {
let lbl = UILabel()
lbl.translatesAutoresizingMaskIntoConstraints = false
lbl.font = .boldSystemFont(ofSize: 22)
lbl.text = "HH:MM:SS"
return lbl
}()
override func viewDidLoad() {
super.viewDidLoad()
totalTime = defaults.integer(forKey: "time")
view.backgroundColor = .white
view.addSubview(timerLbl)
NSLayoutConstraint.activate([
timerLbl.centerXAnchor.constraint(equalTo: view.centerXAnchor),
timerLbl.centerYAnchor.constraint(equalTo: view.centerYAnchor),
timerLbl.heightAnchor.constraint(equalToConstant: 30)
])
// startTimer()
// doBackgroundTask()
UIApplication.shared.runInBackground({
self.startTimer()
}) {
// task after expiration.
}
// DispatchQueue.main.async(execute: {
//
// NotificationCenter.default.addObserver(self, selector:#selector(self.startTimer), name:UIApplication.willEnterForegroundNotification, object: nil)
//
// })
}
// deinit {
// NotificationCenter.default.removeObserver(self)
// }
// var backgroundUpdateTask: UIBackgroundTaskIdentifier?
//
// func doBackgroundTask() {
//
// DispatchQueue.main.async {
//
// self.beginBackgroundUpdateTask()
//
// self.StartupdateLocation()
//
// self.endBackgroundUpdateTask()
//
// }
// }
// func beginBackgroundUpdateTask() {
// self.backgroundUpdateTask = UIApplication.shared.beginBackgroundTask(expirationHandler: {
// self.endBackgroundUpdateTask()
// })
// }
//
// func endBackgroundUpdateTask() {
// if let bgTask = self.backgroundUpdateTask {
// UIApplication.shared.endBackgroundTask(bgTask)
// self.backgroundUpdateTask = UIBackgroundTaskIdentifier.invalid
// }
// }
//
// func StartupdateLocation() {
// startTimer()
// }
let defaults = UserDefaults.standard
@objc func startTimer() {
print(countdownTimer)
countdownTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(updateTime), userInfo: nil, repeats: true)
}
@objc func updateTime() {
print(timerLbl.text as Any)
defaults.set(totalTime, forKey: "time")
let updateTime = defaults.integer(forKey: "time")
timerLbl.text = "\(timeFormatted(updateTime))"
totalTime += 1
defaults.set(totalTime, forKey: "time")
// if totalTime != 0 {
// totalTime += 1
// } else {
// endTimer()
// }
}
func endTimer() {
countdownTimer.invalidate()
}
func timeFormatted(_ totalSeconds: Int) -> String {
let seconds: Int = totalSeconds % 60
let minutes: Int = (totalSeconds / 60) % 60
let hours: Int = totalSeconds / 3600
return String(format: "%02d:%02d:%02d", hours,minutes, seconds)
}
}
extension UIApplication {
/// Run a block in background after app resigns activity
public func runInBackground(_ closure: @escaping () -> Void, expirationHandler: (() -> Void)? = nil) {
DispatchQueue.main.async {
let taskID: UIBackgroundTaskIdentifier
if let expirationHandler = expirationHandler {
taskID = self.beginBackgroundTask(expirationHandler: expirationHandler)
} else {
taskID = self.beginBackgroundTask(expirationHandler: { })
}
closure()
self.endBackgroundTask(taskID)
}
}
}
来源:https://stackoverflow.com/questions/48686960/set-timer-on-swift