【问题标题】:Swift 5 : Wait on Global Queue to finish execution on on main queueSwift 5:等待全局队列在主队列上完成执行
【发布时间】:2021-08-18 00:03:47
【问题描述】:

当最初的开发人员离开组织时,我们从另一个团队获得了一个项目。我需要修复一个缺陷。我有一些类似下面的代码,其中UIGraphicsBeginPDFContextToData 发生在Global 队列上。但这会给出如下内存警告UIView 扩展的renderToImage 不会发生在main 线程上。

我想将 renderToImage 包裹在 DispatchQueue.main.async 中,但它会破坏 someFunction 中的 for 循环。我不能在主线程上使用.sync。谁能告诉我如何完成renderToImage 等待Global 队列?

原始代码:

         DispatchQueue.global(qos: .userInitiated).async {
             let pdfData = NSMutableData()
             UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)

             zip(self.pdfPages, self.pageViews).forEach { (page, view) in

                 let image = view.renderToImage()
                 guard let imageData = image.jpegData(compressionQuality: 0.5),
                     let compressedImage = UIImage(data: imageData) else { return }

                 UIGraphicsBeginPDFPageWithInfo(CGRect(origin: CGPoint.zero, size: page.originSize), nil)
                 compressedImage.draw(in: CGRect(origin: CGPoint.zero, size: page.originSize))

                 DispatchQueue.main.async {
                     self.progressLabel.text = "Processing PDF... \(page.pageNumber) / \(pageNumbers)"
                 }
             }
             UIGraphicsEndPDFContext()

             DispatchQueue.main.async {
                 self.progressIndicator.stopAnimating()
                 self.progressView.isHidden = true
                 completionBlock?(pdfData as Data)
             }
         }


extension UIView {
    func renderToImage() -> UIImage {
        UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0.0)
        guard let context = UIGraphicsGetCurrentContext() else { return UIImage() }
        layer.render(in: context)
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()

        return image!
    }
}

应用@paulw11 回答后:


    fileprivate func generatePDF(_ completionBlock: ((_ pdfData: Data) -> Void)?) {

        let pageNumbers = pageViews.count

        progressView.isHidden = false
        progressIndicator.startAnimating()
        progressLabel.text = "Processing PDF... \(0) / \(pageNumbers)"

        let semaphore = DispatchSemaphore(value: 0)
        let pdfContextQueue = DispatchQueue(label: kMessagesFaxPDFConversionQueue, target: DispatchQueue.global(qos: .userInitiated))

        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)

        pdfContextQueue.async {
            zip(self.pdfPages, self.pageViews).forEach { (page, view) in
                view.renderToImage { image in
                    guard let imageData = image.jpegData(compressionQuality: 0.5),
                    let compressedImage = UIImage(data: imageData) else { return }

                    UIGraphicsBeginPDFPageWithInfo(CGRect(origin: CGPoint.zero, size: page.originSize), nil)
                    compressedImage.draw(in: CGRect(origin: CGPoint.zero, size: page.originSize))

                    DispatchQueue.main.async {
                        self.progressLabel.text = "Processing PDF... \(page.pageNumber) / \(pageNumbers)"
                    }
                    semaphore.signal()
                }
                semaphore.wait()
            }
            UIGraphicsEndPDFContext()

            DispatchQueue.main.async {
                self.progressIndicator.stopAnimating()
                self.progressView.isHidden = true
                completionBlock?(pdfData as Data)
            }
        }
}


extension UIView {
    func renderToImage(completion: @escaping ((UIImage) -> Void)) {
         DispatchQueue.main.async {
             UIGraphicsBeginImageContextWithOptions(self.bounds.size, self.isOpaque, 0.0)

             guard let context = UIGraphicsGetCurrentContext() else {
                 completion(UIImage()); return
             }

             self.layer.render(in: context)
             let image = UIGraphicsGetImageFromCurrentImageContext()
             UIGraphicsEndImageContext()

             completion(image!)
         }
     }
}

【问题讨论】:

  • 你不应该使用NSMutableData swift。只需使用Data
  • @Paulw11: UIGraphicsBeginPDFContextToData 期望 NSMutableData
  • @paulw11:在主队列中使用UIGraphicsGetCurrentContext 并在全局队列中使用UIGraphicsEndPDFContext 是否正确?这就是我没有 pdfData 的原因吗?
  • 它们是不同的上下文。一个是为渲染图像而创建的上下文,另一个是渲染 PDF 的上下文。当您没有生成PDF时,您到底是什么意思?数据的长度是0吗?它是非零但空白的内容吗?

标签: ios swift5


【解决方案1】:

我要检查的第一件事是您是否不能简单地在主队列上执行工作 - 真的需要那么长时间才能对 UI 产生明显影响吗?无论如何,在生成 PDF 时用户还会做什么?如果他们正在等待生成 PDF 以便共享或打印,那么只需显示一个微调器就足够了。

如果您确实想将工作保留在另一个队列中,那么我会将 renderToImage 转换为异步函数并使用 DispatchSemaphore 阻塞您的工作队列,直到循环迭代完成。

func someFunction() {
    let semaphore = DispatchSemaphore(value:0)
    let queue = DispatchQueue(label:"PDFQueue", target: DispatchQueue.global(qos: .userInitiated))
    queue.async {
        for view in PDFViews {
            UIGraphicsBeginPDFContextToData(pdfData, CGRect.zero, nil)
            zip(self.pdfPages, self.pageViews).forEach { (page, view) in
                view.renderToImage { image in 
                   if let image = image {
                      ...
                      ...
                   }
                   semaphore.signal()
                }
                semaphore.wait()
            }
        }
    }   
}

extension UIView {
    func renderToImage(completion: ((UIImage?)->Void)) {
        DispatchQueue.main.async {
            UIGraphicsBeginImageContextWithOptions(bounds.size, isOpaque, 0.0)
            guard let context = UIGraphicsGetCurrentContext() else {
                completion(nil)
            }

            layer.render(in: context)
            let image = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()

            completion(image)
        }
    }   
}


【讨论】:

  • 我建议创建另一个队列来处理任务,然后在主队列上完成 renderImage 而不是阻塞全局队列,target 是全局队列的串行队列可能会很好。 developer.apple.com/documentation/dispatch/dispatchqueue/…
  • @Paulw11 :嗨,这很有帮助。非常感谢。但是,我看到返回的图像是按顺序排列的。但是没有生成 PDF。我已经更新了原始问题中的代码。你介意检查一次吗?在我的原始代码中,UIGraphicsBeginPDFContextToDataUIGraphicsEndPDFContext 发生在不同的线程上。但它仍然很好。你介意检查一下吗?
  • 你有没有设置一些断点来看看发生了什么。有很多地方你可以得到nil
  • @paulw11:实际上我根本没有得到 nil。串行顺序真的很棒。我从来没有使用过信号量。谢谢你。但是没有生成 PDF。
猜你喜欢
  • 1970-01-01
  • 2012-12-01
  • 2022-11-30
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-18
  • 2013-01-19
相关资源
最近更新 更多