Утечки памяти после генерации QR-кода и создания PDF-файла в Swift 4.2 - PullRequest
0 голосов
/ 07 февраля 2019

Я создаю приложение, которое в конце одного цикла генерирует 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 голосов
/ 07 февраля 2019

Из того, что я видел в коде, утечка памяти, кажется, вызвана сильной ссылкой на 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. Создайте свой webView в методе viewDidLoad для контроллера, а не внутри замыкания, или сделайте его 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?

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

PS: я не отвечал на вопрос StackOverflow годами.:)

...