内存泄漏,在do-catch块中。 iOS,Swift

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了内存泄漏,在do-catch块中。 iOS,Swift相关的知识,希望对你有一定的参考价值。

我正在查看我的应用程序中使用视觉检测文本的内存泄漏。

我收到内存泄漏时,使用树指向此行:

try imageRequestHandler.perform([self.textDetectionRequest])

我不确定为什么,希望有人能帮忙解决这个问题。

完整代码如下。

private func performVisionRequest(image: CGImage, orientation: CGImagePropertyOrientation) {

    DispatchQueue.global(qos: .userInitiated).async {
        do {
            var imageRequestHandler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:])
            try imageRequestHandler.perform([self.textDetectionRequest])
        } catch let error as NSError {
            print("Failed to perform vision request: (error)")

        }
    }
}

这是全班:

import UIKit
import Vision

var noText: Bool!
var imageNo: UIImage!

internal class Slicer {

private var image = UIImage()
private var sliceCompletion: ((_ slices: [UIImage]) -> Void) = { _ in }

private lazy var textDetectionRequest: VNDetectTextRectanglesRequest = {

    return VNDetectTextRectanglesRequest(completionHandler: self.handleDetectedText)
}()

internal func slice(image: UIImage, completion: @escaping ((_: [UIImage]) -> Void)) {
    self.image = image
    self.sliceCompletion = completion
    self.performVisionRequest(image: image.cgImage!, orientation: .up)
}

// MARK: - Vision

private func performVisionRequest(image: CGImage, orientation: CGImagePropertyOrientation) {

    DispatchQueue.global(qos: .userInitiated).async {
        do {
            let imageRequestHandler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:])
            try imageRequestHandler.perform([self.textDetectionRequest])
        } catch let error as NSError {
            self.sliceCompletion([UIImage]())
            print("Failed to perform vision request: (error)")

        }
    }
}

private func handleDetectedText(request: VNRequest?, error: Error?) {
    if let err = error as NSError? {
        print("Failed during detection: (err.localizedDescription)")
        return
    }
    guard let results = request?.results as? [VNTextObservation], !results.isEmpty else {

        noText = true
        print("Tony no text found")
        var slices = [imageNo]
        self.sliceCompletion(slices as! [UIImage])
        slices = []
        return }

    noText = false
    self.sliceImage(text: results, onImageWithBounds: CGRect(x: 0, y: 0, width: self.image.cgImage!.width, height: self.image.cgImage!.height))
}

private func sliceImage(text: [VNTextObservation], onImageWithBounds bounds: CGRect) {
    CATransaction.begin()

    var slices = [UIImage]()

    for wordObservation in text {
        let wordBox = boundingBox(forRegionOfInterest: wordObservation.boundingBox, withinImageBounds: bounds)

        if !wordBox.isNull {
            guard let slice = self.image.cgImage?.cropping(to: wordBox) else { continue }
            slices.append(UIImage(cgImage: slice))
        }
    }

    self.sliceCompletion(slices)

    CATransaction.commit()
}

private func boundingBox(forRegionOfInterest: CGRect, withinImageBounds bounds: CGRect) -> CGRect {

    let imageWidth = bounds.width
    let imageHeight = bounds.height

    // Begin with input rect.
    var rect = forRegionOfInterest

    // Reposition origin.
    rect.origin.x *= imageWidth
    rect.origin.y = ((1 - rect.origin.y) * imageHeight) - (forRegionOfInterest.height * imageHeight)

    // Rescale normalized coordinates. Tony adde + 30 to increase the size of rect
    rect.size.width *= imageWidth + 30
    rect.size.height *= imageHeight + 30

    return rect
}
}

enter image description here

答案

其他人告诉你的是正确的。你有两个引用self(Slicer)实例的闭包,你需要在两个闭包中打破保留周期。我认为这一行是一个巨大的错误:

private lazy var textDetectionRequest: VNDetectTextRectanglesRequest = {
    return VNDetectTextRectanglesRequest(completionHandler: self.handleDetectedText)
}()

除了保留周期之外,你什么都得不到。删除那些行!相反,只需在需要时创建匿名函数。替换这个:

try imageRequestHandler.perform([self.textDetectionRequest])

有了这个:

try imageRequestHandler.perform(
    [VNDetectTextRectanglesRequest(completionHandler:{ req, err in
        self.handleDetectedText(request:req, error:err)
    })]
)

如果仍然存在泄漏(我怀疑),则将其更改为

try imageRequestHandler.perform(
    [VNDetectTextRectanglesRequest(completionHandler:{ [weak self] req, err in
        self?.handleDetectedText(request:req, error:err)
    })]
)
另一答案

我想会是这样的:

DispatchQueue.global(qos: .userInitiated).async { [weak self] _ in
    do {
        var imageRequestHandler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:])
        try imageRequestHandler.perform([self?.textDetectionRequest])
    } catch let error as Error {
        print("Failed to perform vision request: (error)")

    }
}
另一答案

self.textDetectionRequestlazy var,它捕获了handleDetectedText,这是一个closure因此你应该使用像[unowned self][weak self]这样的捕获列表来避免保留周期。

    private lazy var textDetectionRequest: VNDetectTextRectanglesRequest = { [unowned self] in
        return VNDetectTextRectanglesRequest(completionHandler: self.handleDetectedText)
    }()

另外,你可以传递VNRequest数组作为依赖在performVisionRequest函数中简化代码

    internal func slice(image: UIImage, completion: @escaping ((_: [UIImage]) -> Void)) {
        self.image = image
        self.sliceCompletion = completion
        let requests = self.textDetectionRequest
        self.performVisionRequest(image: image.cgImage!, orientation: .up, requests: [requests])
    }

    // MARK: - Vision

    private func performVisionRequest(image: CGImage, orientation: CGImagePropertyOrientation, requests: [VNRequest]) {

        DispatchQueue.global(qos: .userInitiated).async {
            do {
                let imageRequestHandler = VNImageRequestHandler(cgImage: image, orientation: orientation, options: [:])
                try imageRequestHandler.perform(requests)
            } catch let error as NSError {
                self.sliceCompletion([UIImage]())
                print("Failed to perform vision request: (error)")
            }
        }
    }
另一答案

在你的capture list封口中使用textDetectionRequest

lazy var textDetectionRequest: VNDetectTextRectanglesRequest = 
         { [weak self] in
             return VNDetectTextRectanglesRequest(completionHandler: self?.handleDetectedText)
         }()

以上是关于内存泄漏,在do-catch块中。 iOS,Swift的主要内容,如果未能解决你的问题,请参考以下文章

在一个 catch 块中处理多个 swift 错误

关于右值的范围和内存泄漏

Swift 中的“do-catch”语句执行四次?

iOS:内存泄漏代码

iOS 内存泄漏排查以及处理

iOS 内存泄漏监测自动化