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

Swift 5全局队列如何等待主线程UIView renderToImage执行完成生成PDF

全局队列执行UIView渲染不打断循环的实现方案

项目中PDF生成逻辑运行在全局队列,因UIView的渲染方法未在主线程执行触发内存告警。直接将渲染逻辑异步派发到主线程会打断原有for循环流程,同时不允许使用主线程同步调度避免死锁,需要实现全局队列等待主线程渲染完成后再继续执行后续逻辑。

原始代码

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!
    }
}

修复后代码

修复思路:使用DispatchSemaphore信号量实现异步等待,改造renderToImage为异步回调形式,渲染逻辑派发到主线程执行完成后通过回调通知,信号量收到信号后继续循环处理下一页。

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!)
         }
     }
}

内容的提问来源于stack exchange,提问作者Kevv Keka

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.07 08:24:02