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

Swift 6中基于Timer的API管理器如何规避回调的@Sendable要求

Swift 6中基于Timer的API管理器如何规避回调的@Sendable要求

我太懂这种升级Swift 6时被@Sendable要求卡壳的感觉了——尤其是你的API管理器已经依赖Timer触发回调,还要改一堆非Sendable的回调类型,完全重构实在太折腾。针对你的场景,有几个不用大改架构就能解决的办法:

1. 把回调调度到MainActor执行(最安全的方案)

Swift 6对@Sendable的要求核心是线程安全,而Timer的闭包默认在主线程的RunLoop上执行。你可以把回调的执行包裹到@MainActor的上下文里,这样既符合线程安全要求,又不用修改回调的类型标注。

修改后的代码示例:

func performRequest<T: Decodable>(
    request: T,
    onSuccess: @escaping (T) -> Void,
    onFailure: @escaping (Error) -> Void,
    onOffline: (() -> Void)? = nil
) {
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { timer in
        // 用MainActor包裹回调执行,规避Sendable检查
        Task { @MainActor in
            if someCondition {
                onSuccess(someResult)
            } else {
                onFailure(someError)
            }
            // 记得根据业务逻辑停止定时器
            if shouldStopTimer {
                timer.invalidate()
            }
        }
    }
}

这个方案的好处是完全符合Swift 6的并发安全规则,不需要任何“不安全”的标记,适合大多数场景。

2. 用@unchecked Sendable标记回调(最快的临时方案)

如果你能确保所有回调只会在同一个线程(比如主线程)执行,不会跨线程传递或逃逸,可以直接给回调加上@unchecked Sendable标记,告诉编译器跳过Sendable检查。

修改后的代码示例:

func performRequest<T: Decodable>(
    request: T,
    onSuccess: @escaping @unchecked Sendable (T) -> Void,
    onFailure: @escaping @unchecked Sendable (Error) -> Void,
    onOffline: (@escaping @unchecked Sendable () -> Void)? = nil
) {
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { @Sendable timer in
        if someCondition {
            onSuccess(someResult)
        } else {
            onFailure(someError)
        }
    }
}

⚠️ 注意:这个标记相当于你向编译器担保“我会负责线程安全”,如果后续代码不小心把回调传到其他线程,可能会引发数据竞争,所以只适合你能完全掌控回调执行上下文的场景。

3. 把回调封装到容器类中(适合复杂回调场景)

如果你的回调逻辑比较复杂,可以把所有回调封装到一个非Sendable的容器类里,用弱引用捕获容器,再通过MainActor调用容器内的回调。这样既避免了直接捕获非Sendable类型,也能清晰管理回调逻辑。

修改后的代码示例:

// 封装回调的容器类
private class CallbackContainer<T: Decodable> {
    let onSuccess: (T) -> Void
    let onFailure: (Error) -> Void
    let onOffline: (() -> Void)?
    
    init(onSuccess: @escaping (T) -> Void, onFailure: @escaping (Error) -> Void, onOffline: (() -> Void)?) {
        self.onSuccess = onSuccess
        self.onFailure = onFailure
        self.onOffline = onOffline
    }
}

func performRequest<T: Decodable>(
    request: T,
    onSuccess: @escaping (T) -> Void,
    onFailure: @escaping (Error) -> Void,
    onOffline: (() -> Void)? = nil
) {
    let container = CallbackContainer(onSuccess: onSuccess, onFailure: onFailure, onOffline: onOffline)
    
    Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { [weak container] timer in
        guard let container = container else {
            timer.invalidate()
            return
        }
        
        Task { @MainActor in
            if someCondition {
                container.onSuccess(someResult)
            } else {
                container.onFailure(someError)
            }
            
            if shouldStopTimer {
                timer.invalidate()
            }
        }
    }
}

这个方案的优势是把回调逻辑和Timer逻辑解耦,同时通过弱引用避免了循环引用的风险,适合回调参数较多、逻辑复杂的场景。

备注:内容来源于stack exchange,提问作者Alisa Martirosyan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 08:34:39