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

Swift倒计时报Escaping closure captures mutating 'self'错误如何解决

解决方案(针对改为class后定时器停止运行问题)

错误根因

  1. 原struct报错是因为值类型不允许逃逸闭包捕获可变self进行修改
  2. 改为class后定时器失效,是因为没有对创建的Timer持有强引用,RunLoop对Timer的引用为弱引用,startTimer方法执行完成后Timer就会被系统释放,自然不会触发回调。同时如果没有处理循环引用问题,也会导致内存泄漏甚至逻辑异常。

修复后完整代码

class CountDownLogic {
    // 强引用持有定时器,防止提前释放
    private var timer: Timer?
    var hours: Int = 0
    var mins: Int = 0
    var secs: Int = 0
    var dateSet: String = ""
    
    func startTimer() {
        // 先清空已有定时器,避免重复创建
        stopTimer()
        // 闭包添加[weak self]避免循环引用
        timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak self] _ in
            guard let self = self else { return }
            if self.secs > 0 {
                self.secs -= 1
            } else if self.mins > 0 {
                self.mins -= 1
                self.secs = 59
            } else if self.hours > 0 {
                self.hours -= 1
                self.mins = 59
                self.secs = 59
            } else {
                // 倒计时结束自动停止定时器
                self.stopTimer()
                return
            }
            self.updateLabel()
        }
    }
    
    func updateLabel() {
        // 格式化输出,避免个位数显示不符合预期
        dateSet = String(format: "%02d:%02d:%02d", hours, mins, secs)
    }
    
    /// 手动停止倒计时
    func stopTimer() {
        timer?.invalidate()
        timer = nil
    }
    
    deinit {
        // 实例销毁时自动停止定时器,避免泄漏
        stopTimer()
    }
}

使用注意事项

  • 在ViewController中使用时,需要将CountDownLogic的实例声明为全局属性(持有强引用),不要在方法内部创建局部实例,否则实例释放后定时器也会停止。
  • 如果需要根据dateSet更新UI,确保更新逻辑在主线程执行:
// 示例UI更新代码
DispatchQueue.main.async {
    self.countDownLabel.text = countDownLogic.dateSet
}

内容的提问来源于stack exchange,提问作者Ali Hameedaldeen

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.04 08:21:01