You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

已耗时计时器(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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 04:19:47