Swift 4如何设置Timer在每日指定时间(23:59)启动并重复执行?
如何让Timer在每日固定23:59启动并重复执行
我来帮你搞定这个问题!你已经找对了Timer重复执行的核心逻辑,现在只需要把首次触发的时间精准设置为下一个23:59的时刻就行,具体拆解如下:
1. 写个工具函数计算目标时间
我们需要借助Calendar和DateComponents来精准计算:如果当前时间还没到今天的23:59,就用今天的这个点;如果已经过了,就自动取明天的23:59。
func getNextFireDate(atHour hour: Int, minute: Int) -> Date { let calendar = Calendar.current // 获取当前日期的年、月、日、时、分组件 var components = calendar.dateComponents([.year, .month, .day, .hour, .minute], from: Date()) // 把时分设置为目标值(23:59) components.hour = hour components.minute = minute var nextFireDate = calendar.date(from: components)! // 如果目标时间已经过了今天,就往后推一天 if nextFireDate < Date() { nextFireDate = calendar.date(byAdding: .day, value: 1, to: nextFireDate)! } return nextFireDate }
2. 用目标时间初始化Timer
调用上面的函数拿到精准的首次触发时间,再传给Timer的fireAt参数就可以实现需求了:
// 获取下一个23:59的时间点 let targetDate = getNextFireDate(atHour: 23, minute: 59) // 初始化Timer,每日重复执行 let timer = Timer(fireAt: targetDate, interval: 86400, target: self, selector: #selector(runCode), userInfo: nil, repeats: true) RunLoop.main.add(timer, forMode: .commonModes) // 你的定时执行方法 @objc func runCode() { print("定时任务执行啦!") }
iOS 10+推荐写法(避免循环引用)
如果你是iOS 10及以上系统,更建议用block形式的Timer API,能规避target-action模式可能带来的循环引用问题:
let targetDate = getNextFireDate(atHour: 23, minute: 59) let timer = Timer(fire: targetDate, interval: 86400, repeats: true) { [weak self] _ in self?.runCode() } RunLoop.main.add(timer, forMode: .commonModes)
小提醒
- 当APP进入后台后,Timer大概率会暂停甚至失效,如果需要后台也能稳定执行任务,得结合iOS的
BGTaskScheduler后台任务机制来处理,这是另一个独立的知识点啦。 - 记得在合适的时机(比如页面销毁、APP退出时)调用
timer.invalidate(),避免内存泄漏。
内容的提问来源于stack exchange,提问作者RJB
相关产品推荐
相关产品推荐

