Swift 3 CGImage 内存问题

3
我在Swift3中编写了以下函数,实际上几乎是从Apple的示例代码中直接翻译过来的。
我的应用程序处理从相机捕获的实时视频数据流中获取的样本缓冲区(sampleBuffers)。
该函数可以正确地创建并返回一个基于CMSampleBuffer的图像。它运行良好,但内存一直增长直到应用程序崩溃。
使用Instruments工具,我发现有一些图像数据没有被释放。如果我注释掉“context.makeImage”这行代码,内存就不会增长。读取该函数文档,它说它从上下文(context)中复制数据。因此我认为有一些数据被复制了,但未释放其一份副本。
问题在于Swift自动处理CoreFoundations内存保留/释放,所以我无法控制它。
如您所见,我尝试使用autoreleasepool,但它没有起作用。
有什么想法吗?
谢谢
func image(from sampleBuffer:CMSampleBuffer) -> UIImage?{

    /* https://developer.apple.com/library/content/qa/qa1702/_index.html */
    let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)

    guard imageBuffer != nil else{
        return nil
    }

    return autoreleasepool{ () -> UIImage in

        CVPixelBufferLockBaseAddress(imageBuffer!, .readOnly);

        let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer!)

        // Get the number of bytes per row for the pixel buffer
        let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!);
        // Get the pixel buffer width and height
        let width = CVPixelBufferGetWidth(imageBuffer!);
        let height = CVPixelBufferGetHeight(imageBuffer!);

        let colorSpace = CGColorSpaceCreateDeviceRGB();

        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
            .union(.byteOrder32Little)

        // Create a bitmap graphics context with the sample buffer data
        let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8,
                                bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue);

        // Create a Quartz image from the pixel data in the bitmap graphics context
        let quartzImage = context!.makeImage();
        // Unlock the pixel buffer
        CVPixelBufferUnlockBaseAddress(imageBuffer!,.readOnly);

        let image = UIImage(cgImage: quartzImage!)

        return image
    }
}

1
你确定问题完全出现在这里,而不是例如在你后续使用“image”时?当然,泄漏的图像可能是在这里创建的,但这并不意味着问题的根源就在这里。你是否使用了Xcode的“调试内存图”功能,并查看了UIImage是否存在?这将向你展示谁在保留UIImage - Rob
尝试使用仪器(Instrument)的分配模式来检查你的代码,就像Rob所说的那样,问题可能出在其他地方。你是将图像存储到数组中吗? - Andrea
@Rob 你是对的,这里的代码完全正常。问题在于我是从后台线程执行 tempImageView.layer.render(in: context!),而 UIKit 不支持多线程。这导致了一些内存泄漏。感谢你指出正确的方向。 - Axy
2个回答

4
我找到了解决问题的方法。
我不知道UIKit不是线程安全的。看起来在与主线程不同的线程中执行该代码,系统无法像应该释放内存。
在主线程上,内存得到正确释放和管理。

1
你具体做了什么? - Pooja M. Bohora
我在主线程上调用了调用此函数的代码,并且内存得到了正确释放。 - Axy
好的,但我的代码已经在主线程上了 :( 有其他解决方案吗? - Pooja M. Bohora
可能是其他问题,你能发一些代码吗? - Axy
我不是操作系统专家,但在StackOverflow上我扮演一个。似乎主线程定期清除自动释放堆分配,而后台线程则不会,除非您使用autorelease {}闭包或线程终止。请参见下面的我的答案。 - Stickley

3

与其他不符合ARC标准的Core* 一样,您可能需要在CM调用中包装autorelease。

func image(from sampleBuffer:CMSampleBuffer) -> UIImage?{
 

    var ret: UIImage?
    autoreleasepool {

        /* https://developer.apple.com/library/content/qa/qa1702/_index.html */
        let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)


        CVPixelBufferLockBaseAddress(imageBuffer!, .readOnly);

        let baseAddress = CVPixelBufferGetBaseAddress(imageBuffer!)

        // Get the number of bytes per row for the pixel buffer
        let bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer!);
        // Get the pixel buffer width and height
        let width = CVPixelBufferGetWidth(imageBuffer!);
        let height = CVPixelBufferGetHeight(imageBuffer!);

        let colorSpace = CGColorSpaceCreateDeviceRGB();

        let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
            .union(.byteOrder32Little)

        // Create a bitmap graphics context with the sample buffer data
        let context = CGContext(data: baseAddress, width: width, height: height, bitsPerComponent: 8,
                                bytesPerRow: bytesPerRow, space: colorSpace, bitmapInfo: bitmapInfo.rawValue);

        // Create a Quartz image from the pixel data in the bitmap graphics context
        let quartzImage = context!.makeImage();
        // Unlock the pixel buffer
        CVPixelBufferUnlockBaseAddress(imageBuffer!,.readOnly);

        let image = UIImage(cgImage: quartzImage!)

        ret = image
    }
    return ret
}

详见此答案


1
很好,由于在后台线程循环中使用CGContext、CGImage及相关类而导致了一个巨大的内存泄漏。你可以通过编写return autoreleasepool { ... }来简化它。 - Peterdk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接