Swift 6中@Sendable闭包捕获非Sendable类型self的问题求解
解决Swift 6中@Sendable闭包捕获非Sendable类型的问题
问题场景
迁移至Swift 6.0时,使用async/await实现网络状态监听的NetworkService类,在setupNetworkMonitoring()方法内出现如下错误:
Capture of 'self' with non-sendable type 'NetworkService' in a @Sendable closure
原代码
public class NetworkService { private let session: URLSessionProtocol private let networkMonitor: NetworkMonitorProtocol private let stateSubject = CurrentValueSubject<NetworkActivityState, Never>(.idle) private var requestQueue: [URLRequest] = [] @MainActor public init(session: URLSessionProtocol, networkMonitor: NetworkMonitorProtocol) { self.session = session self.networkMonitor = networkMonitor Task { await setupNetworkMonitoring() } } public func request(_ urlRequest: URLRequest) async throws -> Data? { // 处理请求,或在离线时将请求加入队列 } // 监听网络状态变化以处理请求 private func setupNetworkMonitoring() async { await networkMonitor.startMonitoring { [weak self] state in guard let self = self else { return } Task { // ⬇ 错误位置:@Sendable闭包捕获非Sendable类型的self try await self.onNetworkStateChanged(networkConnectionState: state) } } } // 处理网络状态变化 public func onNetworkStateChanged(networkConnectionState: NetworkConnectionState) async throws { switch networkConnectionState { case .connected where stateSubject.value == .waitingForConnection: try await executeQueuedRequests() stateSubject.send(NetworkActivityState.idle) case .offline, .restricted: stateSubject.send(NetworkActivityState.waitingForConnection) case .connected, .notDetermined: stateSubject.send(stateSubject.value) } } // 网络恢复后处理队列中的请求 private func executeQueuedRequests() async throws { for urlRequest in requestQueue { _ = try await request(urlRequest) } requestQueue.removeAll() } }
已尝试方案
- 让
NetworkService遵循Sendable协议,但引发大量依赖类需同步修改的连锁反应; - 为类及相关方法添加
@MainActor标记,错误仍未消除; - 考虑使用独立
actor处理网络状态变化,但担心增加过多样板代码。
可行解决方案
方案1:使用@unchecked Sendable临时兼容(最快过渡方案)
直接标记NetworkService为@unchecked Sendable,告诉编译器手动保证类的线程安全性,无需修改依赖:
public class NetworkService: @unchecked Sendable { // 原有代码保持不变 }
注意:使用此方案需确保类内所有状态访问无并发冲突,比如requestQueue的修改和读取需在同一线程执行,stateSubject的发送操作保证线程安全。
方案2:通过@MainActor隔离状态访问(最小侵入方案)
将关键属性和方法绑定到主线程,确保所有状态操作在单线程环境执行,消除并发风险:
- 标记
requestQueue为@MainActor属性:
@MainActor private var requestQueue: [URLRequest] = []
- 将
onNetworkStateChanged标记为@MainActor方法:
@MainActor public func onNetworkStateChanged(networkConnectionState: NetworkConnectionState) async throws { // 原有代码保持不变 }
- 修改
setupNetworkMonitoring中的Task为@MainActor上下文:
private func setupNetworkMonitoring() async { await networkMonitor.startMonitoring { [weak self] state in guard let self = self else { return } Task { @MainActor in try await self.onNetworkStateChanged(networkConnectionState: state) } } }
此方案无需修改类的继承关系,仅通过@MainActor约束确保状态访问的线程安全,符合原有代码结构。
方案3:用独立Actor隔离状态逻辑(长期最优方案)
创建独立actor封装状态管理逻辑,避免NetworkService直接暴露在并发环境中:
// 独立Actor处理网络状态和请求队列 actor NetworkStateManager { private let stateSubject: CurrentValueSubject<NetworkActivityState, Never> private var requestQueue: [URLRequest] = [] private let networkService: NetworkService init(stateSubject: CurrentValueSubject<NetworkActivityState, Never>, networkService: NetworkService) { self.stateSubject = stateSubject self.networkService = networkService } func handleStateChange(_ state: NetworkConnectionState) async throws { switch state { case .connected where stateSubject.value == .waitingForConnection: try await executeQueuedRequests() stateSubject.send(.idle) case .offline, .restricted: stateSubject.send(.waitingForConnection) case .connected, .notDetermined: stateSubject.send(stateSubject.value) } } private func executeQueuedRequests() async throws { for urlRequest in requestQueue { _ = try await networkService.request(urlRequest) } requestQueue.removeAll() } func enqueueRequest(_ request: URLRequest) { requestQueue.append(request) } }
修改NetworkService使用该Actor:
public class NetworkService { private let session: URLSessionProtocol private let networkMonitor: NetworkMonitorProtocol private let stateSubject = CurrentValueSubject<NetworkActivityState, Never>(.idle) private let stateManager: NetworkStateManager @MainActor public init(session: URLSessionProtocol, networkMonitor: NetworkMonitorProtocol) { self.session = session self.networkMonitor = networkMonitor self.stateManager = NetworkStateManager(stateSubject: stateSubject, networkService: self) Task { await setupNetworkMonitoring() } } public func request(_ urlRequest: URLRequest) async throws -> Data? { // 离线时将请求加入Actor队列 if stateSubject.value == .waitingForConnection { await stateManager.enqueueRequest(urlRequest) return nil } // 正常处理请求逻辑 // ... } private func setupNetworkMonitoring() async { await networkMonitor.startMonitoring { [weak self] state in guard let self = self else { return } Task { try await self.stateManager.handleStateChange(state) } } } }
此方案完全符合Swift并发模型,将状态操作隔离在Actor内,避免数据竞争,仅增加少量封装代码,长期维护性更强。
内容的提问来源于stack exchange,提问作者cgontijo
相关产品推荐
相关产品推荐

