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

Swift开发中如何实现后台持续运行Timer定时器?

问题根因

你的实现存在两个核心问题导致定时器仅执行一次就停止:

  • Timer.scheduledTimer依赖当前线程的RunLoop运行,iOS应用进入后台后主线程RunLoop会被暂停,定时器自然无法持续触发
  • 没有主动申请后台运行权限,应用进入后台后最多仅能存活3-5秒就会被系统挂起,所有代码执行都会终止
实现方案

建议使用不受RunLoop影响的GCD定时器,同时主动申请后台运行时长,具体实现如下:

1. 声明必要的属性

// 保存后台任务标识,用于管理后台运行权限
private var backgroundTaskID: UIBackgroundTaskIdentifier = .invalid
// GCD定时器需要强引用,否则会被系统自动释放
private var backgroundTimer: DispatchSourceTimer?
// 你的计数变量
private var secondsCount = 60

2. 修改通知监听(可选优化)

建议将监听的通知改为UIApplication.didEnterBackgroundNotification,避免接听电话、下拉控制中心等临时失活场景误触发逻辑:

NotificationCenter.default.addObserver(self, selector: #selector(enterBackground), name: UIApplication.didEnterBackgroundNotification, object: nil)

3. 重写后台触发方法

@objc func enterBackground() {
    // 清理之前残留的后台任务,避免资源占用
    if backgroundTaskID != .invalid {
        UIApplication.shared.endBackgroundTask(backgroundTaskID)
        backgroundTaskID = .invalid
    }
    
    // 申请后台运行权限
    backgroundTaskID = UIApplication.shared.beginBackgroundTask { [weak self] in
        // 系统分配的后台时长耗尽时触发该回调,必须在这里销毁资源并结束后台任务,否则应用会被系统强制杀死
        guard let self = self else { return }
        self.backgroundTimer?.cancel()
        self.backgroundTimer = nil
        UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
        self.backgroundTaskID = .invalid
    }
    
    // 初始化GCD定时器,默认放在全局队列执行
    backgroundTimer = DispatchSource.makeTimerSource(queue: .global())
    // 配置定时器:立即启动,每1秒触发一次,误差设置为0(对精度要求不高可适当调大更省电)
    backgroundTimer?.schedule(deadline: .now(), repeating: 1, leeway: .milliseconds(0))
    // 配置定时器回调
    backgroundTimer?.setEventHandler { [weak self] in
        guard let self = self else { return }
        // 如果需要操作UI需要切回主线程,仅打印计数可以省略该步
        DispatchQueue.main.async {
            print(self.secondsCount)
            self.secondsCount -= 1
            // 计数为0时主动销毁定时器并结束后台任务
            if self.secondsCount <= 0 {
                self.backgroundTimer?.cancel()
                self.backgroundTimer = nil
                if self.backgroundTaskID != .invalid {
                    UIApplication.shared.endBackgroundTask(self.backgroundTaskID)
                    self.backgroundTaskID = .invalid
                }
            }
        }
    }
    // 启动定时器
    backgroundTimer?.resume()
}
注意事项
  • 普通应用主动申请的后台运行时长最长约为30秒,不同iOS版本会有小幅差异,时长耗尽后应用仍会被系统挂起
  • 如果需要更长时间的后台运行,需要在Xcode的「Signing & Capabilities」中添加匹配业务场景的后台模式权限,比如音频播放、持续定位、VoIP等,没有对应业务权限的应用无法长时间后台存活
  • 后台任务使用完成后必须调用endBackgroundTask主动归还权限,否则系统会直接终止你的应用

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 02:24:05