在 Swift 4.2 中生成 QR 码并制作 PDF 文件后内存泄漏



我正在创建一个应用程序,在一个周期结束时,生成一个QR码,然后创建一个PDF文件。在多次运行该应用程序(测试了近 5 天(后,该应用程序停止执行其其他必需的功能。我发现每次应用程序生成QR并制作PDF时,内存图在应用程序的连续循环中都会变得越来越高。调试内存图指示 3 个内存泄漏:一个在 viewDidAppear 事件,另一个在 createPDF 方法,第三个在生成 QrCode 方法。每次用户开始新周期时,我都会删除PDF文件。我不知道如何处理这些内存泄漏。有人可以帮忙吗?这是代码:

    let html = ""
    override func viewDidAppear(_ animated: Bool) {
        createPDF()
        return
    }
    func createPDF() {
        html = "<div style='display: flex; justify-content: center;'></div>"
        generateQrCode(arg: true, completion: { (success) -> Void in
            print("QR Code Generated")
            let image1 = captureView()
            let imageData1 = image1.pngData() ?? nil
            let base64String1 = imageData1?.base64EncodedString() ?? ""
            if success {
                self.html += "<html><body><div style='text-align: center'><p><br></p><p><b><img width='50%' height='20%' src='data:image/png;base64,(String(describing: base64String1))'/><p><br></p><p><b></b></p></div></body></html>"
                weak var webView = WKWebView(frame: self.view.frame)
                self.view.addSubview(webView!)
                webView!.navigationDelegate = self
                webView!.loadHTMLString(html, baseURL: nil)
                webView!.isUserInteractionEnabled = false
            } else {
                print("false")
            }
        })
    }
    func generateQrCode(arg: Bool, completion: (Bool) -> ()){
        let data = self.dataToQr.data(using: .ascii, allowLossyConversion: false)
        let filter = CIFilter(name: "CIQRCodeGenerator")
        filter?.setValue(data, forKey: "inputMessage")
        let img = UIImage(ciImage: (filter?.outputImage)!)
        self.qrImageView.image = img
        completion(true)
    }
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let render = UIPrintPageRenderer()
        render.addPrintFormatter(webView.viewPrintFormatter(), startingAtPageAt: 0)
        let page = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4, 72 dpi
        let printable = page.insetBy(dx: 0, dy: 0)
        render.setValue(NSValue(cgRect: page), forKey: "paperRect")
        render.setValue(NSValue(cgRect: printable), forKey: "printableRect")
        let pdfData = NSMutableData()
        UIGraphicsBeginPDFContextToData(pdfData, .zero, nil)
        for i in 1...render.numberOfPages {
            UIGraphicsBeginPDFPage();
            let bounds = UIGraphicsGetPDFContextBounds()
            render.drawPage(at: i - 1, in: bounds)
        }
        UIGraphicsEndPDFContext();
        let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0]
        pdfData.write(toFile: "(documentsPath)/(UserDefaults.standard.string(forKey: "yourName")!)_(UserDefaults.standard.string(forKey: "gameName")!)_Kapper.pdf", atomically: true)
        print("(documentsPath)/apper.pdf")
        webView.removeFromSuperview()
    }

从我所看到的代码来看,您的内存泄漏似乎是由您在此闭包中对self的强引用引起的

completion: { (success) -> Void in
            print("QR Code Generated")
            let image1 = captureView()
            let imageData1 = image1.pngData() ?? nil
            let base64String1 = imageData1?.base64EncodedString() ?? ""
            if success {
                self.html += "<html><body><div style='text-align: center'><p><br></p><p><b><img width='50%' height='20%' src='data:image/png;base64,(String(describing: base64String1))'/><p><br></p><p><b></b></p></div></body></html>"
                weak var webView = WKWebView(frame: self.view.frame)
                self.view.addSubview(webView!)
                webView!.navigationDelegate = self
                webView!.loadHTMLString(html, baseURL: nil)
                webView!.isUserInteractionEnabled = false
            } else {
                print("false")
            }
        }

以下是要执行的操作:

  1. 在控制器的 viewDidLoad 方法中创建 webView,而不是在闭包内创建,或者使其成为情节提要上的 IBOutlet。然后将闭包中的self引用设置为弱引用,如下所示。
generateQrCode(arg: true, completion: { [weak self] (success) -> Void in
            print("QR Code Generated")
            let image1 = captureView()
            let imageData1 = image1.pngData() ?? nil
            let base64String1 = imageData1?.base64EncodedString() ?? ""
            if success {
                self?.html += "<html><body><div style='text-align: center'><p><br></p><p><b><img width='50%' height='20%' src='data:image/png;base64,(String(describing: base64String1))'/><p><br></p><p><b></b></p></div></body></html>"
                self?.webView.navigationDelegate = self
                self?.webView.loadHTMLString(html, baseURL: nil)
                self?.webView.isUserInteractionEnabled = false
            } else {
                print("false")
            }
        })

这应该有效。让我知道结果如何。

有关更多上下文,请查看以下链接:如何在带参数的 swift 块中正确处理弱自我

阻止 Swift 中的保留周期?

https://medium.com/@sergueivinnitskii/most-common-memory-leak-trap-in-swift-4565dbae5445

PS:我已经很多年没有回答StackOverflow的问题了。 :)

最新更新