Swift 6中如何将Vision文字识别结果标记为Sendable?
Swift 6并发模式下Vision API文本识别结果的Sendable兼容解决方案
问题场景
我用Vision API实现了图像文字识别功能,通过GCD将耗时的识别操作调度到后台队列,完成后回到主队列执行completion回调:
public struct TextRecognitionResult { let observation: VNRecognizedTextObservation let text: VNRecognizedText let rect: CGRect } public enum TextRecognitionUtil { private static let queue = DispatchQueue(label: "text_recognition", qos: .userInitiated) public static func process( image: UIImage, recognitionLevel: VNRequestTextRecognitionLevel, completion: @Sendable @escaping ([TextRecognitionResult]) -> Void) { guard let cgImage = image.cgImage else { completion([]) return } let request = VNRecognizeTextRequest { (request, error) in guard error == nil, let observations = request.results as? [VNRecognizedTextObservation] else { DispatchQueue.main.async { completion([]) } return } var results = [TextRecognitionResult]() // Vision坐标系原点在左下角,转换为UIKit坐标系 let transform = CGAffineTransform.identity .scaledBy(x: 1, y: -1) .translatedBy(x: 0, y: -image.size.height) .scaledBy(x: image.size.width, y: image.size.height) for observation in observations { guard let text = observation.topCandidates(1).first else { continue } let rect = observation.boundingBox.applying(transform) results += [TextRecognitionResult(observation: observation, text: text, rect: rect)] } DispatchQueue.main.async { completion(results) } } request.recognitionLevel = recognitionLevel self.queue.async { let handler = VNImageRequestHandler(cgImage: cgImage, options: [:]) try? handler.perform([request]) } } }
在Swift 6并发模式下,这段代码触发错误:
Sending 'results' risks causing data races; this is an error in the Swift 6 language mode
核心问题是TextRecognitionResult无法直接标记为Sendable——它包含的VNRecognizedTextObservation和VNRecognizedText是Vision框架定义的类型,无法修改其Sendable属性。
解决方案
方案1:提取必要数据,创建纯Sendable结构体
最安全合规的做法是只提取业务所需的数据,定义一个仅包含Sendable原生类型的新结构体,避免传递框架的非Sendable实例。
比如如果只需要文本内容、置信度和位置信息:
// 定义符合Sendable的纯数据结构体 public struct RecognizedTextInfo: Sendable { let text: String let confidence: Float? let rect: CGRect }
然后在识别回调中转换数据:
var results = [RecognizedTextInfo]() for observation in observations { guard let text = observation.topCandidates(1).first else { continue } let rect = observation.boundingBox.applying(transform) let info = RecognizedTextInfo( text: text.string, confidence: text.confidence, rect: rect ) results.append(info) } DispatchQueue.main.async { completion(results) }
同时更新process方法的completion参数类型:
public static func process( image: UIImage, recognitionLevel: VNRequestTextRecognitionLevel, completion: @Sendable @escaping ([RecognizedTextInfo]) -> Void)
方案2:用@unchecked Sendable标记结构体
如果必须保留Vision框架的原始实例,可以手动标记TextRecognitionResult为@unchecked Sendable——前提是确认这些框架类型本身是线程安全的。
根据Apple官方文档,Vision的识别结果对象是不可变的,生成后可以安全跨队列传递,因此可以安全使用该标记:
public struct TextRecognitionResult: @unchecked Sendable { let observation: VNRecognizedTextObservation let text: VNRecognizedText let rect: CGRect }
注意:使用@unchecked Sendable意味着你需要自行保证结构体所有成员的线程安全性,确认不会引发数据竞争。
内容的提问来源于stack exchange,提问作者OMGPOP
相关产品推荐
相关产品推荐

