Swift 6中如何在Actor内启动定时器实现Ping功能?
在Swift 6后台线程实现定时Ping功能的解决方案
问题原因
你的代码中ping()未被调用的核心原因是:Timer依赖RunLoop运行,但TimerActor执行线程的RunLoop并未启动循环。当调用await actor.start()时,start方法在TimerActor的串行执行线程上运行,虽然你创建了Timer并添加到当前RunLoop,但后台线程的RunLoop默认不会自动启动,导致Timer永远无法触发。
推荐解决方案:使用Swift Concurrency原生实现(无需Timer/RunLoop)
这种方式更贴合Swift 6的并发设计,无需手动管理RunLoop,代码简洁且安全:
actor TimerActor { private var isRunning = false func start() { guard !isRunning else { return } isRunning = true // 启动循环任务,每隔2秒执行一次ping Task { [weak self] in guard let self = self else { return } while self.isRunning { self.ping() // 等待2秒,允许取消 try? await Task.sleep(for: .seconds(2)) } } } func stop() { // 停止循环 isRunning = false } func ping() { print("send ping at \(Date())") } }
使用示例
class ViewController: UIViewController { let actor = TimerActor() override func viewDidLoad() { super.viewDidLoad() Task { await actor.start() } } // 页面销毁时停止定时任务 deinit { Task { await actor.stop() } } }
可选方案:修复Timer+RunLoop的实现(不推荐)
如果一定要使用Timer,需要确保RunLoop持续运行,同时避免阻塞Actor的串行执行:
actor TimerActor { private var timer: Timer? private var runLoopTask: Task<Void, Never>? func start() { guard runLoopTask == nil else { return } runLoopTask = Task { let runLoop = RunLoop.current timer = Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { [weak self] timer in guard let self = self else { timer.invalidate() return } Task { await self.ping() } } runLoop.add(timer!, forMode: .common) // 启动RunLoop,当Task取消或无事件时退出 while !Task.isCancelled && runLoop.run(mode: .common, before: .distantFuture) {} } } func stop() { timer?.invalidate() timer = nil runLoopTask?.cancel() runLoopTask = nil } func ping() { print("send ping at \(Date())") } }
内容的提问来源于stack exchange,提问作者William Hu
相关产品推荐
相关产品推荐

