Swift自定义类中Timer仅在init方法生效,需改用startTimer实现
解决Timer在自定义类方法中无法触发的问题
看起来你遇到了个典型的Timer使用坑:在init里创建的Timer能正常跑,但调用startTimer()方法创建的却完全没反应。我来帮你拆解原因和解决办法:
核心问题根源
Timer.scheduledTimer()虽然会自动把定时器添加到当前线程的RunLoop,但这里有两个容易忽略的关键点:
- 线程差异:主线程的RunLoop默认是持续运行的,所以
init里的Timer能正常触发;但如果startTimer()是在子线程被调用,而该子线程的RunLoop没有被启动,Timer就会“躺平”不工作。 - 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
相关产品推荐
相关产品推荐

