Swift倒计时报Escaping closure captures mutating 'self'错误如何解决
解决方案(针对改为class后定时器停止运行问题)
错误根因
- 原struct报错是因为值类型不允许逃逸闭包捕获可变self进行修改
- 改为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
相关产品推荐
相关产品推荐

