简体   繁体   English

使用malloc的内存泄漏/使用arc释放

[英]memory leak with malloc / free with arc

I work on image processing app for Mac OSX and have problem with huge memory leaks (I'm using ARC). 我在Mac OSX的图像处理应用程序上工作,遇到内存泄漏过多的问题(我正在使用ARC)。 On main window I've an slider which modyfies values of pixels and updates an image. 在主窗口中,我有一个滑块,可以修改像素值并更新图像。 But when I'm changing slider value my app allocates more and more memory (Few minutes of "sliding" allocates even up to 10 GB of memory!). 但是,当我更改滑块值时,我的应用程序分配了越来越多的内存(“滑动”的数分钟甚至分配了多达10 GB的内存!)。

App operates on quite big images (30-40mb) in grayscale. 应用程序可在相当大的灰度图像(30-40mb)上运行。 I've created 3 C Arrays with my pixels and operate on them but I call free but it seems they are not freed. 我用像素创建了3个C数组并对其进行操作,但我调用了free但似乎它们没有被释放。

Changing value of slider fires that method: 更改滑块的值将触发该方法:

-(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];
    });
}

and here are implementation of called methods: 这是被调用方法的实现:

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

I free my C arrays in dealloc: 我在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"];
}

If I use other C arrays in other places I free them in the same method. 如果我在其他地方使用其他C数组,则会以相同的方法释放它们。 It looks like memory is not freed (even when I close current image and open new one) and dealloc is fired. 看起来内存没有被释放(即使当我关闭当前图像并打开新图像时),并且dealloc被触发。 Do you have any idea what is going on? 你知道发生了什么吗? I tear my hair out because of that! 因此,我把头发扯了!

You have a line of code that says: 您有一行代码说:

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

According to the Managing Toll-Free Bridging section of the Transitioning to ARC Release Notes tells us, though, that: 不过,根据《 过渡到ARC发行说明》中的“ 管理免费电话桥接”部分,该信息告诉我们:

__bridge_retained or CFBridgingRetain casts an Objective-C pointer to a Core Foundation pointer and also transfers ownership to you. __bridge_retainedCFBridgingRetain将Objective-C指针强制转换为Core Foundation指针,并将所有权转让给您。 You are responsible for calling CFRelease or a related function to relinquish ownership of the object. 您负责调用CFRelease或相关函数来放弃对象的所有权。

The CFBridgingRetain is simply unneeded here. 这里根本不需要CFBridgingRetain And you're leaking because you're never releasing the CFDataRef you passed to CGDataProviderCreateWithCFData (you release the provider, but not the CFDataRef ). 而你泄露,因为你永远不会释放CFDataRef你传递给CGDataProviderCreateWithCFData (松开提供商,而不是CFDataRef )。 You might as well let ARC retain ownership and clean up for you. 您最好还是让ARC保留所有权并为您清理。 That's easier than adding the code to (a) keep track of the CFDataRef that you transferred ownership to with CFBridgingRetain and then (b) manually CFRelease the CFDataRef . 这比将代码添加到(a)跟踪通过CFDataRef转让所有权的CFBridgingRetain ,然后(b)手动CFRelease CFDataRef来添加代码容易。 You can just change that line as follows, to not transfer ownership at all, and ARC will take care of the rest (for the NSData , that is): 您可以按如下所示更改该行,以完全不转移所有权,而ARC将负责其余部分(对于NSData而言):

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

Unrelated, but createImage is returning _image , which is getting set asynchronously, and thus _image will not be set by the time you hit that return line. 无关,但createImage返回的是_image ,它是异步设置的,因此在您击中该return _image将不会设置_image You could fix this so it would return the appropriate value, but it turns out that you don't use the return value from createImage , so it might be easier to change the return type to void . 您可以解决此问题,使其返回适当的值,但事实证明您没有使用createImage的返回值,因此将返回类型更改为void可能会更容易。 But the current code is just a little confusing, suggesting you're returning something when you're really not. 但是当前代码只是有些混乱,建议您在实际上不是的时候返回一些东西。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM