已耗时计时器(Elapsed Timer)无法启动问题求助
我来帮你搞定这个已耗时计时器启动不了的问题!从你给出的代码片段来看,大概率是几个常见的iOS Timer使用误区或者跨类状态同步的问题导致的,咱们一步步拆解解决:
可能的问题原因及对应解决方案
1. Timer未被强引用导致提前释放
Timer.scheduledTimer 创建的Timer虽然会被RunLoop临时持有,但如果你的ViewController里没有保留这个Timer的强引用,在VC生命周期变化(比如页面跳转)或者系统内存紧张时,Timer很可能被提前释放,导致计时器直接停摆。
解决方法:在ViewController中添加一个Timer属性来持有它:
import UIKit class ViewController: UIViewController { let watch = Stopwatch() @IBOutlet weak var elapsedTimeLabel: UILabel! // 添加Timer引用,用于持有计时器实例 var updateTimer: Timer? @IBAction func startButton(_ sender: Any) { // 先停止之前的Timer,避免重复创建多个计时器 updateTimer?.invalidate() // 启动Stopwatch的计时逻辑 watch.start() // 创建并持有Timer,每隔0.1秒更新一次UI updateTimer = Timer.scheduledTimer( timeInterval: 0.1, target: self, selector: #selector(updateElapsedTime), userInfo: nil, repeats: true ) } @IBAction func stopButton(_ sender: Any) { // 停止Stopwatch计时 watch.stop() // 销毁Timer并释放引用 updateTimer?.invalidate() updateTimer = nil } // 必须用@objc修饰,才能被Timer的selector调用 @objc private func updateElapsedTime() { // 从Stopwatch获取当前已耗时,格式化后更新Label let elapsedTime = watch.elapsedTime elapsedTimeLabel.text = String(format: "%.1f秒", elapsedTime) } }
2. Stopwatch类的核心计时逻辑缺失或错误
你的Stopwatch类需要正确维护计时状态(启动时间、累计时长、运行状态),否则即使Timer在跑,也拿不到正确的已耗时数据。这里给你一个标准的Stopwatch实现:
import Foundation class Stopwatch { // 记录上次启动的时间点 private var startTime: Date? // 记录暂停时累计的时长 private var accumulatedTime: TimeInterval = 0 // 标记当前是否在运行 private var isRunning = false // 对外暴露的已耗时计算属性 var elapsedTime: TimeInterval { if isRunning, let startTime = startTime { // 运行中:累计时长 + 本次启动到现在的时长 return accumulatedTime + Date().timeIntervalSince(startTime) } else { // 已暂停:直接返回累计时长 return accumulatedTime } } func start() { // 避免重复启动 guard !isRunning else { return } startTime = Date() isRunning = true } func stop() { // 避免重复暂停 guard isRunning else { return } // 累加本次运行的时长 accumulatedTime += Date().timeIntervalSince(startTime!) isRunning = false startTime = nil } // 可选:重置计时器 func reset() { accumulatedTime = 0 isRunning = false startTime = nil } }
3. Timer的Selector方法不符合要求
你之前的代码里Timer的selector部分被截断了,要注意:
- 对应的方法必须用
@objc修饰(因为Timer是OC底层实现的) - 如果方法带参数,要确保selector的签名和方法一致(比如带Timer参数的话,selector要写
#selector(updateElapsedTime(_:)))
示例代码(带Timer参数的写法):
@objc private func updateElapsedTime(_ timer: Timer) { // 这里可以通过timer参数做一些额外判断,比如是否是当前的计时器 let elapsedTime = watch.elapsedTime elapsedTimeLabel.text = String(format: "%.1f秒", elapsedTime) } // 创建Timer时对应的写法 updateTimer = Timer.scheduledTimer( timeInterval: 0.1, target: self, selector: #selector(updateElapsedTime(_:)), userInfo: nil, repeats: true )
4. UI更新未在主线程执行
虽然默认情况下Timer会在主线程的RunLoop中运行,但如果你的Stopwatch类里用到了后台线程处理计时逻辑,一定要确保更新UI的代码在主线程执行,否则会导致UI不更新或者崩溃:
@objc private func updateElapsedTime() { DispatchQueue.main.async { [weak self] in guard let self = self else { return } let elapsedTime = self.watch.elapsedTime self.elapsedTimeLabel.text = String(format: "%.1f秒", elapsedTime) } }
内容的提问来源于stack exchange,提问作者user9611921
相关产品推荐
相关产品推荐

