UIGraphicsGetImageFromCurrentImageContext 内存泄漏与预览

Posted

技术标签:

【中文标题】UIGraphicsGetImageFromCurrentImageContext 内存泄漏与预览【英文标题】:UIGraphicsGetImageFromCurrentImageContext memory leak with previews 【发布时间】:2011-07-04 12:22:00 【问题描述】:

我正在尝试在 PDF 中创建页面的预览图像 但是我在释放内存时遇到了一些问题。

我写了一个简单的测试算法来循环这个问题, 应用在第 40 次迭代附近崩溃

NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:@"myPdf.pdf"];
CFURLRef url = CFURLCreateWithFileSystemPath( NULL, (CFStringRef)pdfPath, kCFURLPOSIXPathStyle, NO );
CGPDFDocumentRef myPdf = CGPDFDocumentCreateWithURL( url );
CFRelease (url);
CGPDFPageRef page = CGPDFDocumentGetPage( myPdf, 1 );

int i=0;
while(i < 1000)

    UIGraphicsBeginImageContext(CGSizeMake(768,1024));
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetRGBFillColor(context, 1.0,1.0,1.0,1.0);
    CGContextFillRect(context,CGRectMake(0, 0, 768, 1024));
    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, 1024);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextDrawPDFPage(context, page);
    CGContextRestoreGState(context);

    // --------------------------
    // The problem is here (without this line the application doesn't crash)
    UIImageView *backgroundImageView1 = [[UIImageView alloc] initWithImage:UIGraphicsGetImageFromCurrentImageContext()];
    // --------------------------

    UIGraphicsEndImageContext();
    [backgroundImageView1 release];

    NSLog(@"Loop: %d", i++);


CGPDFDocumentRelease(myPdf);

上述行似乎产生了内存泄漏, 但是,instruments 没有显示内存问题;

我可以摆脱这种错误吗?有人可以用哪种方式解释我吗? 还有其他方法可以显示 pdf 的预览吗?

更新

我认为问题不在于UIGraphicsGetImageFromCurrentImageContext() 方法创建的UIImage 的发布,而是使用此自动释放图像创建的UIImageView 的发布。

我把这行代码分成了三个步骤:

UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIImageView *myImageView = [[UIImageView alloc] init];
[myImageView setImage: myImage]; // Memory Leak

第一行和第二行不会造成内存泄漏,所以我认为 UIGraphicsGetImageFromCurrentImageContext 方法不是问题。

我也尝试了以下方法,但问题仍然存在:

UIImageView *myImageView = [[UIImageView alloc] initWithImage:myImage];

我认为发布包含具有 autorelease 属性的 UIImage 的 UIImageView 存在内存泄漏。

如thread 中所述,我尝试编写继承 UIView 的对象 UIImageView。

此解决方案有效但不是很优雅,这是一种解决方法,我更喜欢使用对象 UIImageView 解决内存问题。

【问题讨论】:

您使用什么工具来检查内存使用情况?我会查看“分配”和“活动监视器”,它们都可以显示泄漏工具遗漏的内存使用情况。另外,这段代码到底应该做什么?您想创建 1000 个图像视图,然后在不使用它们的情况下摆脱它们? 你找到答案了吗?我有同样的问题:S 【参考方案1】:

问题是这样的:

UIGraphicsGetImageFromCurrentImageContext()

返回一个自动释放的UIImage。自动释放池会保留此映像,直到您的代码将控制权返回给运行循环,而您很长一段时间都不会这样做。要解决此问题,您必须在 while 循环的每次迭代(或每隔几次迭代)上创建并排出一个新的自动释放池。

【讨论】:

我尝试在每次迭代时创建和释放 NSAutoreleasePool,但内存问题仍然存在。无论如何感谢您的回复! 为我工作。感谢您的提示! 编码人员(包括我自己)常犯的一个错误是将自动释放池放在循环之外,而不是在循环内部。非常感谢您澄清这一点! ***.com/questions/46903182/…【参考方案2】:

我知道这是一个老问题,但我已经为此苦苦思索了几个小时。在我的应用中反复调用

UIImage *image = UIGraphicsGetImageFromCurrentImageContext()

尽管我调用 image = nil; 在一个循环中确实保留了内存;不确定应用程序在释放之前会保留内存多长时间,但它肯定足以让我的应用程序收到内存警告然后崩溃。

我最终通过将调用/使用来自 UIGraphicsGetImageFromCurrentImageContext() 的图像的代码包装在 @autoreleasepool 中来解决它。所以我有:

@autoreleasepool 
    UIImage *image = [self imageWithView:_outputImageView]; //create the image
    [movie addImage:image frameNum:i fps:kFramesPerSec];    //use the image as a frame in movie
    image = nil;

希望对某人有所帮助。

【讨论】:

【参考方案3】:

为了将来参考,这是我为解决这个问题所做的(在 Swift 4 中测试)。

我为从 Internet 下载的每个新图像(在实用程序队列上)调用下面的函数。在实现自动释放池之前,它会在处理大约 100 个后崩溃。

为简单起见,在 resizeImage 函数中,我删除了除自动释放池和泄漏部分之外的所需代码。

private func resizeImage(image: UIImage, toHeight: CGFloat) -> UIImage 
    return autoreleasepool  () -> UIImage in
        [...]

        let newImage = UIGraphicsGetImageFromCurrentImageContext() //Leaked
        UIGraphicsEndImageContext()

        return newImage!
    


我希望这会有所帮助!

【讨论】:

这与现有答案有何不同?您正在使用自动释放池。至少有两个现有答案说要这样做。 以前的答案没有显示与 Swift 4 兼容的自动释放池实现返回值。至少我无法仅通过查看此页面来找出解决方案,这就是为什么我希望我的回答能帮助其他处于类似情况的人。 好的,但现在循环在哪里?问题是关于在循环中获取图像。如果没有循环,则不需要 自动释放池。我只是想看看我们是否可以让您的回答更有用/更准确。 刚刚编辑了答案以帮助澄清。感谢您的反馈马特。 非常好!这解释得很好。【参考方案4】:

对于那些尝试了上述所有解决方案但仍然存在内存泄漏的人,请检查您是否使用了调度队列。如果是这样,请务必将其 autoreleaseFrequency 设置为 .workItem。或者你在里面设置的自动释放池不会执行。

DispatchQueue(label: "imageQueue", qos: .userInitiated, autoreleaseFrequency: .workItem)

希望它有所帮助,它困扰了我好几个小时,直到我终于意识到是 DispatchQueue 持有该块。

【讨论】:

【参考方案5】:

这段代码是否在主线程上运行? UIGraphicsGetImageFromCurrentImageContext (link) 的文档说它必须以这种方式运行。

【讨论】:

是的,我阅读了 Apple 文档。我在主线程上调用这个函数,谢谢你的回复! 来自UIGraphicsGetImageFromCurrentImageContext 上的最新 UIKit 文档:In ios 4 and later, you may call this function from any thread of your app.【参考方案6】:

你的崩溃行你可以像下面这样更新它

让一个 UIimage 退出循环

rendered_image = UIGraphicsGetImageFromCurrentImageContext();

【讨论】:

以上是关于UIGraphicsGetImageFromCurrentImageContext 内存泄漏与预览的主要内容,如果未能解决你的问题,请参考以下文章