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

时间:2019-02-07 09:41:38

标签: ios swift pdf memory-leaks qr-code

我正在创建一个应用程序,该应用程序在一个周期的末尾会生成QR码,然后创建PDF文件。在多次运行该应用程序(经过近5天的测试)后,该应用程序停止执行其其他必需的功能。我发现,每次应用程序生成QR并生成PDF时,内存图在应用程序的连续周期中都会越来越大。调试内存图指示3个内存泄漏:一个在viewDidAppear事件上发生,另一个在createPDF方法上发生,第三次在generateQrCode方法上发生。每当用户开始新的周期时,我都会删除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()
    }

1 个答案:

答案 0 :(得分:2)

根据我对代码的了解,您的内存泄漏似乎是由您对此闭包中的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")
            }
        })

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

有关更多背景信息,请查看以下链接: How to Correctly handle Weak Self in Swift Blocks with Arguments

Block retain cycles in Swift?

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

P.S:多年来我都没有回答StackOverflow问题。 :)