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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 16:44:53