在iOS上通过HTTP获取图像时,内存不断增长的问题

14
我正在实现一个需要通过HTTP获取大量图片的iOS应用程序。我尝试了几种方法,但不管我做什么,Instruments都显示不断增加的内存分配,并且当我在设备上运行时,应用程序迟早会崩溃。Instruments没有显示任何泄漏。
到目前为止,我已经尝试了以下方法:
- 在NSOperation中使用同步的NSURLConnection获取图像 - 在NSOperation中使用异步的NSURLConnection获取图像 - 在主线程中使用[NSData dataWithContentsOfURL:url]获取图像 - 在NSOperation中使用同步的ASIHTTPRequest获取图像 - 将异步的ASIHTTPRequest添加到NSOperationQueue中获取图像 - 使用completionBlock使用异步的ASIHTTPRequest获取图像
Instrumetns中的调用树显示,在处理HTTP响应时消耗了内存。在异步NSURLConnection的情况下,这在...
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}  
在同步的NSURLConnection情况下,Instruments显示一个不断增长的CFData(存储)条目。与异步的NSURLConnection在类似的代码位置出现了与ASIHTTPRequest相同的问题。[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多线程问题。但是,如果我将图像进行持久化,Instruments会显示出在上述位置不断增加的内存分配。

编辑 II:

与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];
}

你能否提供一个小代码示例来重现这个问题并发布吗? - zoul
我无法做到!尽管Instruments显示所提到的位置内存增加,但只有在使用CoreData缓存图像时行为才会显现。 - sink12
你找到了内存不断增长的原因吗? - uncaught_exceptions
我这里也遇到了同样的问题。尝试了所有列出的解决方案都没有帮助。问题似乎在运行iOS 5.1的iPad 1上更为突出。 - Anurag
你有没有找到解决方法?我也遇到了同样的问题...如果我注释掉核心数据持久化,内存就会被正确地管理和释放。 - radesix
2个回答

9
我们遇到了类似的问题。在通过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(存储)不断增长。顺便说一下,我是在后台线程中执行的(如果有任何区别的话)。 - Bushra Shahid
你是在复制字节还是使用CFDataCreateWithBytesNoCopy?也许你正在保留一个意外的副本。(https://developer.apple.com/library/mac/#documentation/CoreFOundation/Conceptual/CFBinaryData/Tasks/CFWorkingBinaryData.html#//apple_ref/doc/uid/20002120-CJBDBHCB) - Pete Magsig

0
在这种情况下,你看到的正是你应该看到的。-[NSMutableData appendData:]会增加其内部缓冲区的大小以容纳新数据。由于NSMutableData始终位于内存中,这会导致相应的内存使用量增加。你期望得到什么结果呢?
如果这些图像的最终目的地是磁盘上,试试使用NSOutputStream而不是NSMutableData。然后,当你完成后想要显示图像时,可以创建一个指向文件的UIImage

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接