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

Swift自定义类中Timer仅在init方法生效,需改用startTimer实现

解决Timer在自定义类方法中无法触发的问题

看起来你遇到了个典型的Timer使用坑:在init里创建的Timer能正常跑,但调用startTimer()方法创建的却完全没反应。我来帮你拆解原因和解决办法:

核心问题根源

Timer.scheduledTimer()虽然会自动把定时器添加到当前线程的RunLoop,但这里有两个容易忽略的关键点:

  1. 线程差异:主线程的RunLoop默认是持续运行的,所以init里的Timer能正常触发;但如果startTimer()是在子线程被调用,而该子线程的RunLoop没有被启动,Timer就会“躺平”不工作。
  2. RunLoop模式限制:即使在主线程,当界面处于滑动状态(比如UIScrollView滚动)时,RunLoop会切换到UITrackingRunLoopMode,默认添加到default模式的Timer会暂停触发。

针对性修复方案

方案1:强制在主线程创建Timer

如果你的startTimer()可能在子线程被调用,直接切换到主线程操作,确保RunLoop处于运行状态:

func startTimer(){
    DispatchQueue.main.async {
        // 用weak self打破循环引用,避免内存泄漏
        self.timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] _ in
            guard let self = self else { return }
            print("\(self.logClassName): IN CLOSURE 2 \(self.recList.count)")
        }
    }
}

方案2:手动将Timer添加到RunLoop的Common Modes

这种方式能让Timer在界面滑动时也持续触发,同时处理循环引用问题:

func startTimer(){
    // 先销毁旧定时器,避免重复创建导致的逻辑混乱
    self.timer.invalidate()
    
    let timer = Timer(timeInterval: 1, repeats: true) { [weak self] _ in
        guard let self = self else { return }
        print("\(self.logClassName): IN CLOSURE 2 \(self.recList.count)")
    }
    // 添加到主线程RunLoop的通用模式,适配更多场景
    RunLoop.main.add(timer, forMode: .common)
    self.timer = timer
}

额外必做:处理循环引用

你的原代码里闭包直接使用self,会导致Timer和MyClass形成循环引用,最终无法释放实例。上面的方案都用了[weak self]配合guard let self = self来打破循环,一定要记得加上。

收尾建议

记得在不需要Timer的时候及时销毁它,比如在类的销毁方法里:

deinit {
    timer.invalidate()
}

内容的提问来源于stack exchange,提问作者Reimond Hill

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 10:11:44