【问题标题】:NSImage drawInRect and NSView cacheDisplayInRect memory retainedNSImage drawInRect 和 NSView cacheDisplayInRect 内存保留
【发布时间】:2016-02-26 03:42:30
【问题描述】:

我正在处理图像的应用程序。这就像用户最多放置 4 张图像,并根据用户选择的模板对它们进行应用布局。一张图片可能会在最终视图中添加 2-3 次。

布局中的每一个图像都是在NSView中绘制的(NSView的drawRect方法使用drawInRect方法)。现在通过将NSView保存为Image来创建最终图像(通过布局所有图像来组合图像),一切都很好。

现在我面临的问题是,一旦所有处理完成,内存就会被应用程序保留。我使用了仪器分配,我没有看到内存泄漏,但我看到“持久字节”随着应用程序的每个会话而不断增加,并且一个用户报告了 GB 的问题。请看截图。

当我在 Instruments 中进一步调查时,我在下面看到导致内存保留的应用程序的代码快照。都与 ImageIO 和 coreImages 有关。从仪器看下面:

然而,这似乎只是 10.10 及以上系统的问题。在 10.9.x 中测试了相同版本的应用程序,内存使用量保持在 60MB。在应用程序中的会话执行期间,它会达到 200MB,但一旦完成,它就会恢复到 50-60MB,这对于某种应用程序来说是通常的。

[_photoImage drawInRect: self.bounds fromRect: NSZeroRect operation: NSCompositeSourceOver fraction: 1.0 respectFlipped: YES hints: nil];
            _photoImage = nil;

以上代码我用于在 NSView 的 drawRect 方法中绘制图像,而图像中显示的代码用于将 NSView 获取为图像。

更新:经过进一步调查,我发现是 CGImageSourceCreateWithData 正在缓存 NSI​​mage 的 TIFF 数据。我更多地使用下面的代码来裁剪图像,如果我取消注释它,内存消耗就可以正常工作。

NSData *imgData = [imageToCrop TIFFRepresentation];
CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)imgData, NULL);
CGImageRef maskRef =  CGImageSourceCreateImageAtIndex(source, 0, NULL);
CGImageRef imageRef = CGImageCreateWithImageInRect(maskRef, rect);
NSImage *cropped = [[NSImage alloc] initWithCGImage: imageRef size:rect.size];

CGImageRelease(maskRef);
CGImageRelease(imageRef);
CFRelease(source);
//CFRelease( options );
imgData = nil;

我还尝试将 kCGImageSourceShouldCache 显式设置为 false(但默认情况下为 false),但结果相同。 请帮助解决内存保留问题。

【问题讨论】:

  • 看起来我问了一些很容易我应该知道或很难回答的问题。 :(请帮忙,我真的卡住了。

标签: objective-c macos cocoa nsimage nsimagerep


【解决方案1】:

最后经过大量调试发现 CGImageSourceCreateWithData 是在某个地方保留了 NSImage 的 TIFF 数据。当我更改此行时:

CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)imgData, NULL);

CGImageSourceRef source = CGImageSourceCreateWithURL((CFURLRef)[NSURL fileURLWithPath:path], NULL);

一切都刚刚开始正常工作,应用程序的内存使用量从 300MB(6 张图片)下降到 50-60MB,现在是一致的行为。

除了上述更改之外,它仍然会在某处导致内存保留,因此要摆脱它,在所有处理完成后,我将每一层的图像清除为 'nil',这就像魅力一样。我的印象是,将 parent 设为 'nil' 也会发布图像,但那不起作用。

无论如何,如果有人似乎对 drawInRect 或 cacheDisplayInRect 有问题,请确保在以后不需要时清除图像。

2016 年 7 月 2 日更新

我发现 kCGImageSourceShouldCache 在 32 位中默认为 false,在 64 位中默认为 true。通过将其设置为 false,我能够使用以下代码释放内存。

const void *keys[] =   { kCGImageSourceShouldCache};
    const void *values[] = { kCFBooleanFalse};
    CFDictionaryRef optionsDictionary = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);

    CGImageSourceRef source = CGImageSourceCreateWithData((__bridge CFDataRef)[image TIFFRepresentation], optionsDictionary);

希望对某人有所帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-01
    • 1970-01-01
    • 2013-11-11
    • 1970-01-01
    • 1970-01-01
    • 2011-05-13
    相关资源
    最近更新 更多