Swift 6下@Sendable闭包捕获Timer的兼容方案咨询
解决Swift 6中Timer在@Sendable闭包的捕获错误
错误原因是Swift 6严格并发模式下,Timer未遵循Sendable协议,但Timer的触发闭包默认被标记为@Sendable。当你在evaluateJavaScript的回调(同样为@Sendable闭包)中捕获timer时,编译器会因类型不匹配抛出错误。
以下是符合Swift 6规范的修改代码:
func updateBuyAmountWithJsValue(url: String, delaySeconds: Double, maxTries: Int, js: String) { // 用Box包装可变值,确保在逃逸闭包中能正确更新状态 let triesBox = Box(value: 0) Timer.scheduledTimer(withTimeInterval: delaySeconds, repeats: true) { [weak self, url, maxTries, js] timer in triesBox.value += 1 guard let self = self else { timer.invalidate() return } // 检查终止条件,满足则销毁定时器 if self.webView.url?.absoluteString != url || triesBox.value > maxTries { timer.invalidate() return } self.webView.evaluateJavaScript(js) { [timer] result, error in // 标记timer为@unchecked Sendable:确认invalidate方法在主队列执行,无并发风险 let timer = timer as @unchecked Sendable if let error = error { print("Error executing JavaScript: \(error)") } else if let value = result as? String { if value.contains(".") || (triesBox.value == maxTries && value.count < 10) { self.updateBuyProgress("AMOUNT*" + value) timer.invalidate() } } } } } // 辅助包装类,用于在逃逸闭包中持有可变值 private class Box<T> { var value: T init(value: T) { self.value = value } }
修改要点:
- 使用
weak self避免循环引用,同时符合并发安全要求。 - 用
Box类包装tries变量:Swift中值类型在逃逸闭包中捕获时会被复制,无法保持状态更新,通过引用类型的Box可以解决这个问题。 - 对
timer添加@unchecked Sendable标记:由于Timer.invalidate()始终在主队列执行,不存在并发访问风险,因此可以安全绕过编译器的Sendable检查。
内容的提问来源于stack exchange,提问作者Daniele B
相关产品推荐
相关产品推荐

