UIGraphicsGetImageFromCurrentImageContext 预览内存泄漏

发布于 2024-10-19 09:37:33 字数 2451 浏览 3 评论 0原文

我正在尝试创建 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 的释放code> 使用此自动释放映像创建。

我已将代码行分为三个步骤:

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

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

我也尝试如下,但问题仍然存在:

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

我认为在释放包含具有 autorelease 属性的 UIImage 的 UIImageView 时存在内存泄漏。

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

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

I'm trying to create previews images of pages in a PDF
but I have some problems with the release of memory.

I wrote a simple test algorithm that cycles on the problem,
the app crashes near the 40th iteration:

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);

The above-mentioned line seems to generate a memory leak,
however, instruments doesn't show memory problems;

Can I escape from this kind of mistake?someone can explain me in which way?
Are there other ways to show previews of a pdf?

UPDATE

I think the problem isn't the release of UIImage created by the method UIGraphicsGetImageFromCurrentImageContext() but the release of UIImageView created with this autorelease image.

I have divided the line of code in three steps:

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

The first and second lines doesn't create memory leaks so I think that the method UIGraphicsGetImageFromCurrentImageContext is not the problem.

I also tried as follows but the problem persists:

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

I think there is a memory leak in the release of a UIImageView that contains a UIImage with the autorelease property.

I tried to write my object UIImageView inheriting a UIView as explained in this thread.

This solution works but isn't very elegant, it's a workaround, I would prefer to use the object UIImageView solving the memory problem.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

楠木可依 2024-10-26 09:37:33

问题是这样的:

UIGraphicsGetImageFromCurrentImageContext()

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

The problem is this:

UIGraphicsGetImageFromCurrentImageContext()

returns an autoreleased UIImage. The autorelease pool holds on to this image until your code returns control to the runloop, which you do not do for a long time. To solve this problem, you would have to create and drain a fresh autorelease pool on every iteration (or every few iterations) of your while loop.

ま柒月 2024-10-26 09:37:33

我知道这是一个老问题,但我已经在这个问题上用头撞墙几个小时了。在我的应用程序中,尽管我调用 image = nil; ,但在循环中重复调用

UIImage *image = UIGraphicsGetImageFromCurrentImageContext()

确实会保留内存。不确定应用程序在释放内存之前会保留内存多长时间,但这肯定足以让我的应用程序收到内存警告然后崩溃。

我最终通过将调用/使用 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;
}

希望这可以帮助别人。

I know it's an old question, but I've just been banging my head against the wall on this for a few hours. In my app repeatedly calling

UIImage *image = UIGraphicsGetImageFromCurrentImageContext()

in a loop does hold on to the memory despite me calling image = nil; Not sure how long the app would keep hold of the memory before freeing, but it's certainly long enough for my app to get a memory warning then crash.

I managed to solve it finally by wrapping the code that calls / uses the image from UIGraphicsGetImageFromCurrentImageContext() in @autoreleasepool. So I have:

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

Hope that might help someone.

撕心裂肺的伤痛 2024-10-26 09:37:33

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

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

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

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

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

        return newImage!
    }

}

我希望这有帮助!

For future reference here's what I did to solve this (tested in Swift 4).

I was calling the function below for every new image downloaded from the internet (on a utility queue). Before implementing the autorelease pool it would crash after processing about 100.

For simplicity, in the resizeImage function I've removed needed code except for the autoreleasepool and the part that was leaking.

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

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

        return newImage!
    }

}

I hope this helps!

画尸师 2024-10-26 09:37:33

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

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

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

For those who tried all solution above and still has a memory leak, check if you are using a dispatch queue. If so, be sure to set its autoreleaseFrequency to .workItem. Or the autorelease pool you set up inside the will not execute.

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

Hope it helps, it has bugged me for hours until I finally realize that's DispatchQueue that is holding the block.

日记撕了你也走了 2024-10-26 09:37:33

这段代码是在主线程上运行的吗? UIGraphicsGetImageFromCurrentImageContext 的文档 (链接)表示它必须以这种方式运行。

Is this code running on the main thread? The documentation of the UIGraphicsGetImageFromCurrentImageContext (link) says it must run that way.

音盲 2024-10-26 09:37:33

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

获取一个 UIimage 退出循环

render_image = UIGraphicsGetImageFromCurrentImageContext();

your line of crash you can update it like following

get one UIimage out of loop

rendered_image = UIGraphicsGetImageFromCurrentImageContext();

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文