如何使用Published变量动态调整DispatchQueue.asyncAfter的延迟时间?
问题分析
你当前的代码中,DispatchQueue.main.asyncAfter会在调用瞬间计算deadline的最终值(即调用时的.now() + logoutTimer),后续修改logoutTimer的数值不会影响已经提交的异步任务——因为任务的执行时间在创建时就已经确定了,所以动态调整计时器无法生效。
解决方案
要实现动态调整延迟时间的登出逻辑,核心是每次logoutTimer变化时,取消之前的延迟任务并重新创建新任务。结合Combine框架(因为logoutTimer是@Published变量),可以轻松实现这个逻辑:
完整实现代码
import Combine class UserSession: ObservableObject { @Published var logoutTimer: TimeInterval = 300 // 默认5分钟 @Published var status: SessionStatus = .active private var cancellables = Set<AnyCancellable>() private var logoutWorkItem: DispatchWorkItem? init() { // 监听logoutTimer的变化 $logoutTimer .sink { [weak self] newTimer in // 取消之前未执行的登出任务 self?.logoutWorkItem?.cancel() // 创建新的登出任务 let workItem = DispatchWorkItem { [weak self] in self?.status = .none } self?.logoutWorkItem = workItem // 提交新的延迟任务 DispatchQueue.main.asyncAfter(deadline: .now() + newTimer, execute: workItem) } .store(in: &cancellables) } } // 示例SessionStatus枚举 enum SessionStatus { case active, none }
关键细节说明
- 取消旧任务:用
DispatchWorkItem封装登出逻辑,每次logoutTimer更新时,先调用cancel()取消之前的任务,避免旧任务在新时间设置后仍执行。 - 弱引用避免循环:闭包中使用
[weak self],防止UserSession对象因被闭包持有而无法释放。 - Combine订阅:通过
$logoutTimer(@Published变量的投影属性)监听数值变化,自动触发任务更新。
替代方案(单次触发Timer)
如果更倾向于使用Timer而非DispatchQueue,也可以用Timer.publish实现单次触发:
init() { $logoutTimer .flatMap { timer in Timer.publish(every: timer, on: .main, in: .common) .autoconnect() .prefix(1) // 只触发一次 } .sink { [weak self] _ in self?.status = .none } .store(in: &cancellables) }
内容的提问来源于stack exchange,提问作者Abhishek Naidu
相关产品推荐
相关产品推荐

