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

关于Set<AnyCancellable>的Sendable合规性及移除@unchecked Sendable替代方案的技术咨询

关于Set的Sendable合规性及移除@unchecked Sendable替代方案的技术咨询

嘿,这个问题我之前帮不少开发者捋清楚过,咱们一步步拆解解决:

首先得明白为什么编译器要求你加@unchecked Sendable——虽然AnyCancellable本身是符合Sendable协议的,但你声明的Set<AnyCancellable>是可变且非线程安全的。多个线程同时读写这个集合会引发数据竞争,编译器没法自动验证这个类的线程安全性,所以只能让你用@unchecked来手动确认。

现在你不想用@MainActor绑定主线程,也不想把整个类改成Actor,那最直接的解决方案就是给这个集合加一层线程安全的包装,用轻量级锁来同步所有读写操作,彻底消除数据竞争。

具体实现方案

自己写一个简单的线程安全集合类,用OSUnfairLock(比串行队列更高效的轻量级锁)来保护底层的Set<AnyCancellable>:

import Combine
import os.lock

final class ThreadSafeCancellableSet {
    private var underlyingSet = Set<AnyCancellable>()
    private let lock = OSUnfairLock()
    
    // 插入新的Cancellable
    func insert(_ cancellable: AnyCancellable) {
        lock.lock()
        defer { lock.unlock() }
        underlyingSet.insert(cancellable)
    }
    
    // 移除指定Cancellable
    func remove(_ cancellable: AnyCancellable) {
        lock.lock()
        defer { lock.unlock() }
        underlyingSet.remove(cancellable)
    }
    
    // 清空所有Cancellable
    func removeAll() {
        lock.lock()
        defer { lock.unlock() }
        underlyingSet.removeAll()
    }
}

// 扩展一下,让它兼容Combine的store(in:)语法
extension ThreadSafeCancellableSet {
    static func += (set: inout ThreadSafeCancellableSet, cancellable: AnyCancellable) {
        set.insert(cancellable)
    }
}

然后修改你的TestSendable类,替换掉原来的Set:

final class TestSendable: Sendable {
    private var subscriptions = ThreadSafeCancellableSet()
    
    // 示例用法,完全不用跳主线程
    func setupAsyncTask() {
        URLSession.shared.dataTaskPublisher(for: URL(string: "https://example.com")!)
            .sink(
                receiveCompletion: { completion in
                    // 处理完成逻辑
                },
                receiveValue: { data in
                    // 处理数据逻辑
                }
            )
            .store(in: &subscriptions) // 直接用Combine的标准语法
    }
}

为什么这个方案可行?

所有对underlyingSet的读写操作都被锁保护起来了,同一时间只有一个线程能访问它,彻底避免了数据竞争。编译器现在可以明确验证这个类的线程安全性,所以你完全可以去掉@unchecked Sendable,直接让类符合Sendable协议。

而且这个方案的性能开销极小,OSUnfairLock是苹果专门设计的轻量级锁,比用DispatchQueue串行队列的开销低很多,完全不用担心对性能造成影响。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.07 11:03:05