【问题标题】:Swift 3 CGImage Memory issueSwift 3 CGImage 内存问题
【发布时间】:2017-06-23 08:46:17
【问题描述】:

我的 Swift3 代码中有这个函数,实际上我几乎是从 Apple 示例代码中翻译过来的。

我的应用程序处理来自从摄像头捕获实时馈送的 sampleBuffers。

此函数正确地从 CMSampleBuffer 创建并返回图像。 它工作正常,但内存一直在增长,直到应用程序崩溃。

通过 Instruments,我看到有一些图像数据没有发布。 如果我评论我执行“context.makeImage”的行,内存就会保持不变。阅读该 func 文档,它说它从上下文中复制数据。所以我在想有一些数据被复制了,一个副本没有发布。

“问题”是 Swift 自动处理 CoreFoundations 内存保留/释放,所以我没有办法处理它。

如您所见,我尝试使用自动释放池,但它不起作用。

有什么想法吗?

谢谢

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
    }
}

【问题讨论】:

  • 您是否 100% 确定问题出在此处,而不是例如在您随后使用 image 时?当然,泄漏的图像可能是在这里创建的,但这并不意味着问题的根源就在这里。您是否使用过 Xcode 的“调试内存图”功能并查看UIImage 是否存在?这将向您显示 UIImage 保留的内容。
  • 尝试使用 Instrument 特别分配来检查您的代码,正如 Rob 所说,也许问题出在其他地方。您是否将图像存储到数组中?
  • @Rob 你是对的,这里的代码工作得很好。问题是我在后台线程中执行此tempImageView.layer.render(in: context!),并且 UIKit 不是线程安全的。所以这留下了一些记忆挂在某个地方。感谢您为我指明正确的方向。

标签: ios memory-leaks swift3 core-image


【解决方案1】:

与其他 Core* libraries which are not ARC compliant 一样,您可能需要将 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
}

详情请见this answer

【讨论】:

  • 很好,由于在后台线程循环中使用 CGContext 和 CGI​​mage 以及相关类,内存泄漏很大。你可以写return autoreleasepool { ... }来简化它
【解决方案2】:

我找到了解决问题的方法。

我不知道 UIKit 不是线程安全的。似乎在与主线程不同的线程中执行该代码不允许系统按应有的方式释放内存。

在主线程上,内存被正确释放和管理。

【讨论】:

  • 你到底做了什么?
  • 我在主线程上调度了调用这个函数的代码并且内存被正确释放了。
  • 好的.. 但我的代码已经在主线程上:( 任何其他解决方案
  • 可能是其他问题,能不能发一些代码?
  • 我不是操作系统专家,但我在 StackOverflow 上玩过。看来主线程会定期清除自动释放堆分配,而后台线程则不会,除非您使用 autorelease {} 闭包或线程终止。请参阅下面的答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-21
  • 2017-03-14
  • 2017-10-06
  • 1970-01-01
  • 2011-05-28
  • 1970-01-01
相关资源
最近更新 更多