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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.15 18:34:50