Swift、内存解决了 CFDataGetBytePtr 的问题

标签 swift

使用 CFDataGetBytePtr 时遇到一些问题。当我同时使用它将 2 批图像数据加载到内存中进行像素颜色评估时,我发现一个数据集与另一个数据集相同,然后我做了一些挖掘,两个数据地址是相同的,我非常不确定为什么。这是我处理这些事情的类(class)。

import Foundation
import UIKit

/// Helper class finds pixels values within the given image
class ImagePixelHelper
{
/// The image to find pixels in
private let image: UIImage

/// The image data
private var data:UnsafePointer<UInt8>?

/// The expected length of the image data
private var expectedLengthA:Int?

/// The expected rgb length of the image
private var expectedLengthRGB:Int?

/// The expected rgba length of the image
private var expectedLengthRGBA:Int?

/// The actual number of bytes in the image
private var numBytes: CFIndex?

/// Default constructor for the ImagePixelHelper
/// - Parameter image: The image to find pixels in
init(image: UIImage)
{
    self.image = image
}

/// Function loads all the image data for quick access later
func loadImageData() throws
{
    // Get the image as a CGImage
    let cgImage : CGImage = image.cgImage!

    // Get the pixel data
    guard let pixelData = CGDataProvider(data: (cgImage.dataProvider?.data)!)?.data else{
        fatalError()
    }

    // Get the pointer to the start of the array
    data = CFDataGetBytePtr(pixelData)!

    // Calculate the expected lengths
    expectedLengthA = Int(image.size.width * image.size.height)
    expectedLengthRGB = 3 * expectedLengthA!
    expectedLengthRGBA = 4 * expectedLengthA!

    // Get the length of the data
    numBytes = CFDataGetLength(pixelData)
}

/// Function sets all member vars to nil to help speed up GC
func unloadImageData()
{
    data = nil
    expectedLengthA = nil
    expectedLengthRGB = nil
    expectedLengthRGBA = nil
    numBytes = nil
}

/// Function gets the pixel colour from the given image using the provided x y coordinates
/// - Parameter pixelX: The X Pixel coordinate
/// - Parameter pixelY: The Y Pixel coordinate
/// - Parameter bgr: Whether we should return BGR, by default this is true so must be set if you want RGB
func getPixelValueFromImage(pixelX: Int, pixelY:Int, bgr: Bool = true) -> UIColor
{
    // If we have all the required member vars for this operation
    if  let data = self.data,
        let expectedLengthA = self.expectedLengthA,
        let expectedLengthRGB = self.expectedLengthRGB,
        let expectedLengthRGBA = self.expectedLengthRGBA,
        let numBytes = self.numBytes
    {
        // Get the index of the pixel we want
        let index = Int(image.size.width) * pixelY + pixelX

        // Check the number of bytes
        switch numBytes
        {
            case expectedLengthA:
                return UIColor(red: 0, green: 0, blue: 0, alpha: CGFloat(data[index])/255.0)
            case expectedLengthRGB:
                if bgr
                {
                    return UIColor(red: (CGFloat(data[3*index+2])/255.0).rounded(), green: (CGFloat(data[3*index+1])/255.0).rounded(), blue: (CGFloat(data[3*index])/255.0).rounded(), alpha: 1.0)
                }
                else
                {
                    return UIColor(red: (CGFloat(data[3*index])/255.0).rounded(), green: (CGFloat(data[3*index+1])/255.0).rounded(), blue: (CGFloat(data[3*index+2])/255.0).rounded(), alpha: 1.0)
                }
            case expectedLengthRGBA:
                if bgr
                {
                    return UIColor(red: (CGFloat(data[4*index+2])/255.0).rounded(), green: (CGFloat(data[4*index+1])/255.0).rounded(), blue: (CGFloat(data[4*index])/255.0).rounded(), alpha: (CGFloat(data[4*index+3])/255.0).rounded())
                }
                else
                {
                    return UIColor(red: (CGFloat(data[4*index])/255.0).rounded(), green: (CGFloat(data[4*index+1])/255.0).rounded(), blue: (CGFloat(data[4*index+2])/255.0).rounded(), alpha: (CGFloat(data[4*index+3])/255.0).rounded())
                }
            default:
                // unsupported format
                return UIColor.clear
        }
    }
    else
    {
        // Something didnt load properly or has been destroyed
        return UIColor.clear
    }
}
}

因此,当我加载 2 个图像时,我会执行以下操作

   let image1 = UIImage(named: "image1")!
   let pixelHelper1 = BviNumberImagePixelHelper(image: image1)
   try pixelHelper1()

   let image2 = UIImage(named: "image2")!
   let pixelHelper1 = BviNumberImagePixelHelper(image: UIImage(named: "image2")!)
   try pixelHelper1()

当此代码执行时,我在它后面有一个断点,并评估两个 PixelHelper 对象上的 ImagePixelHelper.data 的值,内存地址是相同的。

对这个问题有什么想法吗?这意味着当我循环图像时,它们会给出结果

最佳答案

问题在于

let pixelData = CGDataProvider(data: (cgImage.dataProvider?.data)!)?.data

CFData 实例被分配给局部变量,因此在 loadImageData() 函数返回时被释放。然后,下一次调用(可能使用不同的图像)重用相同的内存。

分配

pixelData = CGDataProvider(data: (cgImage.dataProvider?.data)!)?.data

实例属性反而解决了问题。现在,只要 ImagePixelHelper 实例存在,数据就会保留。

您可以进一步简化为

pixelData = cgImage.dataProvider?.data

关于Swift、内存解决了 CFDataGetBytePtr 的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58452487/

相关文章:

ios - 如何自动生成基于类的对象 iOS Swift

swift - Facebook边框疑虑

ios - 设置UIlabel的高度

ios - 在 UIViewController 之间传递 UILongPressGestureRecognizer

ios - 其 View 不在窗口层次结构中

ios - UICollisionBehavior 边界不起作用

iOS 13 替代 'setAnimationCurve'

swift - NS表达式自定义

ios - 如何以编程方式获取 UIVIewController 的 IBOutlet 列表

ios - Swift iOS - 标签 Collection View