计算属性-如何仅在主线程上使用?

时间:2018-06-25 21:44:29

标签: ios swift multithreading avfoundation

我正在尝试使用AVCaptureVideoPreviewLayer捕获视频,但是,当我运行我的应用程序时,主线程检查器会导致崩溃。

必须在什么时候在主线程上运行代码-我假设这将解决我遇到的问题。使用DispatchQueue.main.async({})进行了几次尝试,但均无济于事。

发生错误:

  

主线程检查器:在后台线程上调用的UI API:

原文:

    var videoPreviewLayer: AVCaptureVideoPreviewLayer {

    let previewlayer = layer as! AVCaptureVideoPreviewLayer

    switch gravity {
      case .resize:
        previewlayer.videoGravity = AVLayerVideoGravity.resize
      case .resizeAspect:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
      case .resizeAspectFill:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    }
    return previewlayer
   }

尝试修复:

var videoPreviewLayer: AVCaptureVideoPreviewLayer {

  getVideoPreviewLayer { (previewLayer) in
    return previewLayer
  }
}

func getVideoPreviewLayer( completion: @escaping (AVCaptureVideoPreviewLayer) -> ()) {

  DispatchQueue.main.async { [unowned self ] in

    let previewlayer = self.layer as! AVCaptureVideoPreviewLayer

    switch self.gravity {
    case .resize:
      previewlayer.videoGravity = AVLayerVideoGravity.resize
    case .resizeAspect:
      previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
    case .resizeAspectFill:
      previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    }

    completion(previewlayer)
  }
}

原始 enter image description here 尝试修复 enter image description here

3 个答案:

答案 0 :(得分:1)

这是一种解决方案,可确保无论使用哪个队列访问videoPreviewLayer,您都只能在主队列上获得该层:

var videoPreviewLayer: AVCaptureVideoPreviewLayer {
    let previewlayer: AVCaptureVideoPreviewLayer = {
        if Thread.current.isMainThread {
            return layer as! AVCaptureVideoPreviewLayer
        } else {
            DispatchQueue.main.sync {
                return layer as! AVCaptureVideoPreviewLayer
            }
        }
    }()

    switch gravity {
    case .resize:
        previewlayer.videoGravity = AVLayerVideoGravity.resize
    case .resizeAspect:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
    case .resizeAspectFill:
        previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
    }
    return previewlayer
}

答案 1 :(得分:0)

如错误消息中所述,您尝试的修复程序将无法编译,因为它期望返回值,同时您将此getter包装在异步调度中。

这里的重点不是如何定义吸气剂,而是如何使用它。只要您记得这是与UI相关的API,并且只需要从主线程调用它,代码的原始部分就可以了。因此,与其将代码的这一部分弄乱,不如将对videoPreviewLayer的调用包装在DispatchQueue.main.async调用中,如下所示:

DispatchQueue.main.async {
    let _ = _something_.videoPreviewLayer
}

答案 2 :(得分:0)

您应该使用DispatchQueue.main.sync,因为您要等待响应(您要返回结果值)。使用异步会导致您的代码在获得值之前继续执行。

您也不需要处理单独的功能。

这是一个应该起作用的简单修复程序:

var videoPreviewLayer: AVCaptureVideoPreviewLayer {
    let capturedLayer = DispatchQueue.main.sync {
        previewlayer = layer as! AVCaptureVideoPreviewLayer

        switch gravity {
            case .resize:
                previewlayer.videoGravity = AVLayerVideoGravity.resize
            case .resizeAspect:
                previewlayer.videoGravity = AVLayerVideoGravity.resizeAspect
            case .resizeAspectFill:
                previewlayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        }
        return previewlayer
    }
    return capturedLayer
}

但是,尝试使用此函数返回的图层可能会遇到问题,因为您将离开主线程。最好将DispatchQueue.main.sync移至其他地方的主线程(在上下文中获得videoPreviewLayer变量的某个地方)。