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
相关产品推荐
相关产品推荐

