1

我在 Mac OSX 的图像处理应用程序上工作,并且遇到了巨大的内存泄漏问题(我正在使用 ARC)。在主窗口上,我有一个滑块,它可以修改像素值并更新图像。但是当我更改滑块值时,我的应用程序分配了越来越多的内存(几分钟的“滑动”分配甚至高达 10 GB 的内存!)。

应用程序在相当大的灰度图像(30-40mb)上运行。我用我的像素创建了 3 个 C 数组并对它们进行操作,但我打电话free但似乎它们没有被释放。

更改滑块的值会触发该方法:

-(void)changeCurrentMinOrMax
{
    imageProcessQueue = dispatch_queue_create("rwt.tz", NULL);
    dispatch_async(imageProcessQueue, ^{
        // Change display range
        [self setDisplayRangeWithMin:_currentMin andMax:_currentMax];
        // Pack pixels into filtered raw image data
        [self packPixelsIntoFilteredImageData];
        // Create filtered image data
        [self createImage:_rawFilteredImageData];
    });
}

这里是调用方法的实现:

- (void)setDisplayRangeWithMin:(int)min andMax:(int)max
{
    // Calculate number of gray levels
    NSUInteger numberOfGrayLevels = (NSUInteger)pow(2, _bitsPerPixel);
    // Calculate display range
    int range = max - min;
    // Set treshold
    for (unsigned long i = 0; i < numberOFPixels; i++) {
        if (originalPixels[i] < min) {
            pixels[i] = min;
        } else if (originalPixels[i] > max) {
            pixels[i] = max;
        } else {
            pixels[i] = originalPixels[i];
        }
        // map it again into 0-65535 values of gray
        pixels[i] = (UInt16)((numberOfGrayLevels - ZERO_INDEX) * (float)((pixels[i] - min) / (float)range));
    }
}

- (void)packPixelsIntoFilteredImageData
{
    UInt8 *revertedImageDataArray = malloc(sizeOfBitmap);
    unsigned long j = 0;
    for (unsigned long i = 0; i < sizeOfBitmap; i += 2) {
        revertedImageDataArray[i] = (UInt8)((pixels[j] & 0xFF00) >> 8);
        revertedImageDataArray[i+1] = (UInt8)(pixels[j] & 0x00FF);
        j++;
    }

    // pack an array into NSData again
    _rawFilteredImageData = [NSData dataWithBytes:revertedImageDataArray
                                           length:sizeOfBitmap];
    free(revertedImageDataArray);
    revertedImageDataArray = NULL;
}

- (NSImage *)createImage:(NSData *)imgData
{
    _bitsPerComponent = [imgData length] / (_width * _height) * BYTE_SIZE;
    _bitsPerPixel = _bitsPerComponent;
    _bytesPerRow = _width * _bitsPerPixel / BYTE_SIZE;

    CGDataProviderRef provider =
    CGDataProviderCreateWithCFData((CFDataRef)CFBridgingRetain(imgData));
    // which colorspace is better?
    CGColorSpaceRef colorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericGrayGamma2_2);
    //CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceGray();
    CGImageRef imageRef = CGImageCreate((size_t)_width,
                                        (size_t)_height,
                                        _bitsPerComponent,
                                        _bitsPerPixel,
                                        _bytesPerRow,
                                        colorSpace,
                                        kCGImageAlphaNone,
                                        provider,
                                        NULL,
                                        false,
                                        kCGRenderingIntentDefault);
    CGColorSpaceRelease(colorSpace);
    CGDataProviderRelease(provider);

    NSSize size = NSMakeSize((CGFloat) _width, (CGFloat)_height);

    dispatch_async(dispatch_get_main_queue(), ^{
        [self willChangeValueForKey:@"image"];
        _image = [[NSImage alloc] initWithCGImage:imageRef
                                             size:size];
        [self didChangeValueForKey:@"image"];
        CGImageRelease(imageRef);
    });
    return _image;
}

我在 dealloc 中释放了我的 C 数组:

- (void)dealloc
{
    // Free memory allocated for C arrays
    if (pixels) {
        free(pixels);
        pixels = NULL;
    }
    if (originalPixels){
        free(originalPixels);
        originalPixels = NULL;
    }
    if (imageDataArray8) {
        free(imageDataArray8);
        imageDataArray8 = NULL;
    }
    // Remove observers
    [self removeObserver:self forKeyPath:@"currentMax"];
    [self removeObserver:self forKeyPath:@"currentMin"];
}

如果我在其他地方使用其他 C 数组,我会以相同的方法释放它们。看起来内存没有被释放(即使我关闭当前图像并打开新图像)并且触发了 dealloc。你知道发生了什么吗?因为那个,我把头发扯掉了!

4

1 回答 1

2

你有一行代码说:

CGDataProviderRef provider = CGDataProviderCreateWithCFData((CFDataRef)CFBridgingRetain(imgData));

不过,根据过渡到 ARC 发行说明的管理免费桥接部分,告诉我们:

__bridge_retainedCFBridgingRetain将 Objective-C 指针转换为 Core Foundation 指针,并将所有权转移给您。您负责调用CFRelease或相关函数以放弃对象的所有权。

这里CFBridgingRetain根本不需要。而且您正在泄漏,因为您永远不会释放CFDataRef传递给的内容CGDataProviderCreateWithCFData(您释放了提供者,但没有释放CFDataRef)。您不妨让 ARC 保留所有权并为您清理。这比将代码添加到 (a) 跟踪CFDataRef您将所有权转移到的.CFBridgingRetain然后 (b) 手动添加. 您可以按如下方式更改该行,根本不转移所有权,ARC 将处理其余部分(对于,即):CFReleaseCFDataRefNSData

CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)imgData);

不相关,但createImage正在返回_image,这是异步设置的,因此在您点击该行时_image不会设置。return您可以修复此问题,使其返回适当的值,但事实证明您不使用 from 的返回值createImage,因此将返回类型更改为 . 可能更容易void。但是当前的代码有点令人困惑,这表明您实际上没有返回某些内容。

于 2013-10-12T22:11:03.817 回答