我正在尝试在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的预览?
更新
我认为问题不在于方法UIImage
创建的UIGraphicsGetImageFromCurrentImageContext()
版本,而是使用此自动释放图片创建的UIImageView
版本。
我将代码行分为三个步骤:
UIImage *myImage = UIGraphicsGetImageFromCurrentImageContext();
UIImageView *myImageView = [[UIImageView alloc] init];
[myImageView setImage: myImage]; // Memory Leak
第一行和第二行不会造成内存泄漏,所以我认为方法UIGraphicsGetImageFromCurrentImageContext不是问题。
我也尝试了如下但问题仍然存在:
UIImageView *myImageView = [[UIImageView alloc] initWithImage:myImage];
我认为UIImageView的发行版中存在内存泄漏,其中包含具有自动释放属性的UIImage。
我尝试编写我的对象UIImageView继承UIView,如本thread中所述。
这个解决方案有效,但不是很优雅,这是一种解决方法,我宁愿使用UIImageView对象来解决内存问题。
答案 0 :(得分:26)
问题在于:
UIGraphicsGetImageFromCurrentImageContext()
返回自动释放的UIImage
。自动释放池保留此图像,直到您的代码将控制权返回到runloop,这是您很长时间没有做到的。要解决此问题,您必须在while
循环的每次迭代(或每几次迭代)中创建并排空新的自动释放池。
答案 1 :(得分:12)
我知道这是一个古老的问题,但我刚刚将头撞在墙上几个小时。在我的应用中反复调用
UIImage *image = UIGraphicsGetImageFromCurrentImageContext()
尽管我调用了image = nil,但循环中的确实保留了内存;不确定应用程序在释放之前会持续多长时间,但是对于我的应用程序获取内存警告然后崩溃肯定足够长。
我设法通过在@autoreleasepool中包装调用/使用来自UIGraphicsGetImageFromCurrentImageContext()的图像的代码来最终解决它。所以我有:
@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;
}
希望这可能有助于某人。
答案 2 :(得分:2)
为解决此问题,我做了以下工作(在Swift 4中进行了测试)。
对于从互联网下载的每个新图像(在实用程序队列中),我正在调用以下函数。在实施自动释放池之前,它在处理大约100个之后将崩溃。
为简单起见,在resizeImage函数中,我删除了所需的代码,除了autoreleasepool和正在泄漏的部分。
private func resizeImage(image: UIImage, toHeight: CGFloat) -> UIImage {
return autoreleasepool { () -> UIImage in
[...]
let newImage = UIGraphicsGetImageFromCurrentImageContext() //Leaked
UIGraphicsEndImageContext()
return newImage!
}
}
我希望这会有所帮助!
答案 3 :(得分:2)
对于那些尝试了上述所有解决方案但仍然存在内存泄漏的用户,请检查您是否正在使用调度队列。如果是这样,请确保将其autoreleaseFrequency设置为.workItem。否则,您在中设置的自动释放池将不会执行。
DispatchQueue(label: "imageQueue", qos: .userInitiated, autoreleaseFrequency: .workItem)
希望它会有所帮助,它困扰了我好几个小时,直到我终于意识到DispatchQueue可以容纳该块。
答案 4 :(得分:1)
此代码是否在主线程上运行? UIGraphicsGetImageFromCurrentImageContext(link)的文档说它必须以这种方式运行。
答案 5 :(得分:0)
我在使用大图像时遇到了同样的问题。向图像添加填充或应用过滤器会产生200MB的内存分配,而不会在图像显示在屏幕上时释放它。
找到解决此问题的有效方法:
extension UIImage {
func release() -> UIImage? {
guard let data = UIImageJPEGRepresentation(self, 1.0) else { return nil }
return UIImage(data: data)
}
}
也尝试使用UIImagePNGRepresentation,但它似乎也没有释放分配。
答案 6 :(得分:-1)
您的崩溃线可以像下面的
一样更新让一个UIimage脱离循环
rendered_image = UIGraphicsGetImageFromCurrentImageContext();