在 iOS 中通过 HTTP 获取图像时内存分配不断增加

Posted

技术标签:

【中文标题】在 iOS 中通过 HTTP 获取图像时内存分配不断增加【英文标题】:Constantly growing memory allocation while fetching images over HTTP in iOS 【发布时间】:2011-06-22 18:39:14 【问题描述】:

我正在实现一个需要通过 HTTP 获取大量图像的 ios 应用。我尝试了几种方法,但独立于我所做的事情,Instuments 显示内存分配不断增加,当我在设备上运行它时,应用程序迟早会崩溃。 Instruments 显示没有泄漏。

到目前为止,我已经尝试了以下方法:

在 NSOperation 中使用同步 NSURLConnection 获取图像 在 NSOperation 中使用异步 NSURLConnection 获取图像 在主线程中使用 [NSData dataWithContentsOfURL:url] 获取图像 在 NSOperation 中使用同步 ASIHTTPRequest 获取图像 使用异步 ASIHTTPRequest 获取图像并将其添加到 NSOperationQueue 使用异步 ASIHTTPRequest 并使用 completionBlock 获取图像

Instrumetns 中的调用树显示在处理 HTTP-Response 时会消耗内存。在异步 NSURLConnection 的情况下,这是在

- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data 
[receivedData appendData:data];
  

在同步 NSURLConnection 的情况下,Instruments 会显示一个不断增长的 CFData(存储)条目。 ASIHTTPRequest 的问题似乎与类似代码位置中的异步 NSURLConnection 相同。 [NSData dataWithContentsOfURL:url] 方法恰好在该语句中显示了总内存分配量的增加。

当请求在单独的线程中完成时,我正在使用 NSAutoReleasePool,并且我尝试使用 [[NSURLCache sharedURLCache] removeAllCachedResponses] 释放内存 - 没有成功。

有解决问题的想法/提示吗?谢谢。

编辑: 仅当我使用 CoreData 保留图像时,才会显示该行为。这是我作为 NSInvocationOperation 运行的代码:

-(void) _fetchAndSave:(NSString*) imageId 
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *url = [NSString stringWithFormat:@"%@%@", kImageUrl, imageId];
HTTPResponse *response = [SimpleHTTPClient GET:url headerOrNil:nil];
NSData *data = [response payload];

if(data && [data length] > 0) 
    UIImage *thumbnailImage = [UIImage imageWithData:data];
    NSData *thumbnailData = UIImageJPEGRepresentation([thumbnailImage scaleToSize:CGSizeMake(55, 53)], 0.5); // UIImagePNGRepresentation(thumbnail); 

    [self performSelectorOnMainThread:@selector(_save:) withObject:[NSArray arrayWithObjects:imageId, data, thumbnailData, nil] waitUntilDone:NO];

[pool release];

所有与 CoreData 相关的东西都在这里的主线程中完成,所以不应该有任何 CoreData 多线程问题。但是,如果我保留图像,仪器会在上述位置显示不断增加的内存分配。

编辑二:

CoreData相关代码:

-(void) _save:(NSArray*)args 
NSString *imageId = [args objectAtIndex:0];
NSData *data = [args objectAtIndex:1];
NSData *thumbnailData = [args objectAtIndex:2];

Image *image = (Image*)[[CoreDataHelper sharedSingleton] createObject:@Image];
image.timestamp =  [NSNumber numberWithDouble:[[NSDate date] timeIntervalSince1970]];
image.data = data;

Thumbnail *thumbnail = (Thumbnail*)[[CoreDataHelper sharedSingleton] createObject:@"Thumbnail"];
thumbnail.data = thumbnailData;
thumbnail.timestamp = image.timestamp;
[[CoreDataHelper sharedSingleton] save];

来自 CoreDataHelper(self.managedObjectContext 正在挑选当前线程中可用的 NSManagedObjectContext):

-(NSManagedObject *) createObject:(NSString *) entityName 
return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.managedObjectContext];

【问题讨论】:

你能用small code sample 重现问题并发布吗? 我做不到! Altough Instruments 在上述位置显示内存增加,该行为仅在我使用 CoreData 缓存图像时才会出现。 你找到内存不断增长的原因了吗? 这里有同样的问题。尝试了所有列为答案的解决方案,但没有帮助。这个问题在运行 iOS 5.1 的 iPad 1 上显然更为突出 您找到解决方案了吗?同样的问题......如果我评论核心数据持久性内存被管理并从操作中适当释放 【参考方案1】:

我们遇到了类似的问题。在通过 http 获取大量图像时,内存分配出现了巨大的增长和锯齿模式。我们会看到系统或多或少地在进行清理,但速度很慢,而且无法预测。与此同时,下载量不断涌入,堆积在记忆中的任何东西上。内存分配将达到 200M 左右,然后我们就死了。

问题是 NSURLCache 问题。您说您尝试了 [[NSURLCache sharedURLCache] removeAllCachedResponses]。我们也尝试过,但后来尝试了一些不同的方法。

我们的下载是在 N 个图像/电影的组中完成的,其中 N 通常是 50 到 500 个。重要的是我们获得所有 N 作为原子操作。

在开始我们的 http 下载组之前,我们这样做了:

NSURLCache *sharedCache = [[NSURLCache alloc] initWithMemoryCapacity:0 diskCapacity:0 diskPath:0];
[NSURLCache setSharedURLCache:sharedCache];

然后我们通过 http 同步调用获取 N 中的每个图像。我们在 NSOperation 中进行此组下载,因此我们不会阻止 UI。

NSData *movieReferenceData = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error]; 

最后,在每个单独的图像下载之后,并且在我们完成了该图像的 NSData 对象之后,我们调用:

[sharedCache removeAllCachedResponses]; 

我们的内存分配峰值行为下降到非常舒适的几兆字节,并且停止增长。

【讨论】:

我也做了同样的事情,但我的 cfdata(存储)不断增长。我在 bg 线程 btw 中这样做(如果它有任何差异) 您是在复制字节还是使用 CFDataCreateWithBytesNoCopy?也许你正挂在一份你没想到的副本上。 (developer.apple.com/library/mac/#documentation/CoreFOundation/…)【参考方案2】:

在这种情况下,您看到的正是您应该看到的。 -[NSMutableData appendData:] 增加其内部缓冲区的大小以保存新数据。由于NSMutableData 始终位于内存中,这会导致内存使用量相应增加。你期待什么?

如果这些图像的最终目的地在磁盘上,请尝试使用NSOutputStream 而不是NSMutableData。如果您想显示图像,您可以在完成后创建一个指向该文件的UIImage

【讨论】:

即使我释放了 NSMutableData 对象,内存也没有被释放。 其他人可能持有对NSMutableData 实例的引用。我无法从您发布的代码中看出。此外,不能保证操作系统会在您完成后立即将内存标记为已释放。在许多情况下,它将保持分配状态,以便可以再次使用它(而不是 malloc() 必须调用 sbrk() 这可能会很慢。)

以上是关于在 iOS 中通过 HTTP 获取图像时内存分配不断增加的主要内容,如果未能解决你的问题,请参考以下文章

使用 SharpZipLib 在 .net 中通过 http 流式传输 zip 文件

在 iOS 中通过 Cellular 进行跟踪时获取错误坐标

如何在 iOS 中通过 UIActivityViewController 导出 PHAsset 视频

在 Django 模板中通过 Item.objects.all 显示图像

如何在 ios sdk 中通过动画增加和减少图像的 alpha 值?

在iOS中通过GET方法发送http请求,如果参数中有中文或者空格的时候应该怎么办?