From 9530af2a19d609384ed320ab1db68351d1248083 Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Sat, 3 Oct 2026 03:36:39 +0800 Subject: [PATCH] fix(helper): box XPC reply closure to cross @Sendable boundary MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit @objc 协议 SystemProxyHelperControlling 里 reply 参数无法标注 @Sendable, 直接被 queue.async 的 @Sendable 闭包捕获即产生并发警告。装箱为 @unchecked Sendable 后跨过边界:XPC 运行时生成的应答块线程安全且恰好 调用一次,引擎在串行工作队列上调用它本就是既有设计,行为不变。 --- .../Domain/SystemProxyHelperEngine.swift | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/ShadowsocksX-NG2/Domain/SystemProxyHelperEngine.swift b/ShadowsocksX-NG2/Domain/SystemProxyHelperEngine.swift index e68dd306..3c9afcda 100644 --- a/ShadowsocksX-NG2/Domain/SystemProxyHelperEngine.swift +++ b/ShadowsocksX-NG2/Domain/SystemProxyHelperEngine.swift @@ -34,14 +34,16 @@ final class SystemProxyHelperEngine: NSObject, SystemProxyHelperControlling { } func apply(_ payload: Data, withReply reply: @escaping (Data) -> Void) { - queue.async { [perform] in + let reply = ReplyBox(reply) + queue.async { [perform, reply] in let request = Result { try SystemProxyHelperWire.decodeConfiguration(payload) } reply(Self.responseData(request: request.map { .apply($0) }, perform: perform)) } } func clear(withReply reply: @escaping (Data) -> Void) { - queue.async { [perform] in + let reply = ReplyBox(reply) + queue.async { [perform, reply] in reply(Self.responseData(request: .success(.clear), perform: perform)) } } @@ -72,3 +74,18 @@ final class SystemProxyHelperEngine: NSObject, SystemProxyHelperControlling { } } } + +/// XPC 应答闭包的 Sendable 桥。@objc 协议里 reply 参数无法标注 @Sendable, +/// 但 XPC 运行时生成的应答块线程安全且恰好调用一次,引擎在串行工作队列上 +/// 调用它;装箱只为跨过 queue.async 的 @Sendable 边界。 +private final class ReplyBox: @unchecked Sendable { + private let reply: (Data) -> Void + + init(_ reply: @escaping (Data) -> Void) { + self.reply = reply + } + + func callAsFunction(_ data: Data) { + reply(data) + } +}