Swift 4:从 DispatchQueue.main(范围)访问变量

Posted

技术标签:

【中文标题】Swift 4:从 DispatchQueue.main(范围)访问变量【英文标题】:Swift 4: Access Variables from DispatchQueue.main (Scope) 【发布时间】:2017-12-07 13:09:29 【问题描述】:

我有一个 CoreML 图像分类任务,它从 ios 设备的 [视频] 摄像头获取“实时流”并在后台发生。一旦确定了对象,并且发生了其他应用程序逻辑,我想用一些数据更新 UI 的标签。

有人可以解释对DispatchQueue.main.asyc(execute: ) 的标注如何能够访问我一直在使用的变量吗?我认为这本质上是一个范围界定问题?

我目前使用的代码:

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) 

    processCameraBuffer(sampleBuffer: sampleBuffer)



func processCameraBuffer(sampleBuffer: CMSampleBuffer) 

    let coreMLModel = Inceptionv3()

    if let model = try? VNCoreMLModel(for: coreMLModel.model) 
        let request = VNCoreMLRequest(model: model, completionHandler:  (request, error) in
            if let results = request.results as? [VNClassificationObservation] 

                var counter = 0
                var otherVar = 0

                for item in results[0...9] 

                    if item.identifier.contains("something") 
                        print("some app logic goes on here")
                        otherVar += 10 - counter
                    
                    counter += 1

                
                switch otherVar 
                case _ where otherVar >= 10:
                    DispatchQueue.main.async(execute: 
                        let displayVarFormatted = String(format: "%.2f", otherVar / 65 * 100)
                        self.labelPrediction.text = "\(counter): \(displayVarFormatted)%"
                    )
                default:
                    DispatchQueue.main.async(execute: 
                        self.labelPrediction.text = "No result!"
                    )
                
            
        )

            if let pixelBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) 
                let handler = VNImageRequestHandler(cvPixelBuffer: pixelBuffer, options: [:])
                do 
                    try handler.perform([request])
                 catch 
                    print(error.localizedDescription)
                
            
    

导致问题的 switch 语句中的 self.labelPrediction.text = "" 行。此 var 当前始终为 0。

【问题讨论】:

尝试在您提到的行上放置一个断点,看看变量包含什么。 (通常,块捕获它们需要的值。) 【参考方案1】:

这不是DispatchQueue 的问题。从processCameraBuffer(sampleBuffer:),您的代码会在获得结果之前更新您的 UI。

要解决这个问题,您需要使用escaping closure。您的函数应该如下所示。

func processCameraBuffer(sampleBuffer: CMSampleBuffer, completion: @escaping (Int, String) -> Void) 
    // 2.
    let request = VNCoreMLRequest(model: model, completionHandler:  (request, error) in

      DispatchQueue.main.async(execute: 
          // 3.
          let displayVarFormatted = String(format: "%.2f", otherVar / 65 * 100)
          completion(counter, displayVarFormatted)
      )
    


func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)  
    // 1.
    processCameraBuffer(sampleBuffer)  counter, displayVarFormatted in
        /*
         This Closure will be executed from 
         completion(counter, displayVarFormatted)
        */
        // 4.
        self.labelPrediction.text = "\(counter): \(displayVarFormatted)%"
    

变量的范围从这里不是问题。您需要处理异步任务。

    捕获发生。 processCameraBuffer 被调用,VNCoreMLRequest 被执行。 您将获取数据并执行processCameraBuffer 的完成块,由completion() 执行。 更新标签。

【讨论】:

谢谢@changnam-hong。此函数从 iOS 设备摄像头的“实时流”中调用: func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) processCameraBuffer(sampleBuffer: sampleBuffer) 所以这在每帧捕获时执行.它需要不断地重新扫描视频流,以便不断地进行图像分类。这会改变答案吗? 我已经实现了我的代码以遵循这种格式。但是,在第 4 步 XCode 仍然抱怨 self.labelPrediction.text = "" 行仍然需要在主线程上运行。当我将它改回 DispatchQueue.main.async 时,这个变量再一次永远不会设置(或设置为 0)。

以上是关于Swift 4:从 DispatchQueue.main(范围)访问变量的主要内容,如果未能解决你的问题,请参考以下文章

swift 使用Swift 4从用户设备访问相机,照片库,视频和文件

无法转换任何类型?要键入 AnyObject?从 swift 3 转换为 swift 4 时

如何在 Xcode 中将 Swift 版本从 5 更改为 4?

从 swift 4 调用贝宝 javascript 函数

从Swift 3.2迁移到Swift 4我使用autosavesInPlace收到错误

将Realm从Swift 4转换为Swift 3