在 iOS 中通过 HTTP 获取图像时不断增加内存分配
我正在实现一个 iOS 应用程序,需要通过 HTTP 获取大量图像。我尝试了几种方法,但独立于我所做的,仪器显示内存分配不断增加,并且当我在设备上运行它时,应用程序迟早会崩溃。仪器显示没有泄漏。
到目前为止,我已经尝试了以下方法:
- 在 NSOperation 中使用同步 NSURLConnection 获取图像
- 在 NSOperation 中使用异步 NSURLConnection
- 获取图像 在主线程中使用 [NSData dataWithContentsOfURL:url]
- 获取图像 使用同步 ASIHTTPRequest 获取图像在 NSOperation 中
- 使用异步 ASIHTTPRequest 获取图像并将其添加到 NSOperationQueue
- 使用异步 ASIHTTPRequest 并使用completionBlock 获取图像
Instrumetns 中的调用树显示处理 HTTP 响应时会消耗内存。对于异步 NSURLConnection,这是
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
在同步 NSURLConnection 的情况下,Instruments 显示不断增长的 CFData(存储)条目。 ASIHTTPRequest 的问题似乎与类似代码位置中的异步 NSURLConnection 的问题相同。 [NSData dataWithContentsOfURL:url] 方法显示该语句中总内存分配量不断增加。
当请求在单独的线程中完成时,我正在使用 NSAutoReleasePool,并且我尝试使用 [[NSURLCache sharedURLCache] removeAllCachedResponses] 释放内存 - 没有成功。
有解决问题的想法/提示吗?谢谢。
编辑: 仅当我使用 CoreData 保留图像时,该行为才会出现。这是我作为 NSInvocableOperation 运行的代码:
-(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 多线程问题。但是,如果我保留图像,仪器会在上述位置显示不断增加的内存分配。
编辑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.managementObjectContext正在选择当前线程中可用的NSManagedObjectContext):
-(NSManagedObject *) createObject:(NSString *) entityName {
return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.managedObjectContext];
}
I am implementing an iOS App that needs to fetch a huge amount of images over HTTP. I've tried several approaches but independently what I do, Instuments shows constantly increasing memory allocations and the App crashes sooner or later when I run it on a device. There are no leaks shown by Instruments.
So far I have tried the following approches:
- Fetch the images using a synchronous NSURLConnection within an NSOperation
- Fetch the images using a asynchronous NSURLConnection within an NSOperation
- Fetch the images using [NSData dataWithContentsOfURL:url] in the Main-Thread
- Fetch the images using synchronous ASIHTTPRequest within an NSOperation
- Fetch the images using asynchronous ASIHTTPRequest and adding it to a NSOperationQueue
- Fetch the images using asynchronous ASIHTTPRequest and using a completionBlock
The Call Tree in Instrumetns shows that the memory is consumed while processing the HTTP-Response. In case of asynchronous NSURLConnection this is in
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
[receivedData appendData:data];
}
In case of the synchronous NSURLConnection, Instruments shows a growing CFData (store) entry.
The problem with ASIHTTPRequest seems to be the same as with the asynchronous NSURLConnection in a analogous code-position. The [NSData dataWithContentsOfURL:url] approach shows an increasing amount of total memory allocation in exactely that statement.
I am using an NSAutoReleasePool when the request is done in a separate thread and I have tried to free up memory with [[NSURLCache sharedURLCache] removeAllCachedResponses] - no success.
Any ideas/hints to solve the problem? Thanks.
Edit:
The behaviour only shows up if I persist the images using CoreData. Here is the code I run as a 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];
}
All CoreData related stuff is done in the Main-Thread here, so there should not be any CoreData multithreading issue. However, if I persist the images, Instruments shows constantely increasing memory allocations at the positions described above.
Edit II:
CoreData related code:
-(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];
}
From CoreDataHelper (self.managedObjectContext is picking the NSManagedObjectContext usable in the current thread):
-(NSManagedObject *) createObject:(NSString *) entityName {
return [NSEntityDescription insertNewObjectForEntityForName:entityName inManagedObjectContext:self.managedObjectContext];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我们也有类似的问题。当通过 http 获取大量图像时,内存分配出现了巨大的增长和锯齿状模式。我们会看到系统或多或少地清理干净,但速度很慢,而且不可预测。与此同时,下载的内容源源不断地涌入,内存中的内容不断堆积起来。内存分配会达到 200M 左右,然后我们就会死掉。
问题是 NSURLCache 问题。您表示您尝试过 [[NSURLCache sharedURLCache] removeAllCachedResponses]。我们也尝试过,但后来尝试了一些不同的东西。
我们的下载是以 N 个图像/电影为一组进行的,其中 N 通常为 50 到 500 个。重要的是我们获得所有 N 个图像/电影。作为原子操作。
在开始我们的 HTTP 下载组之前,我们这样做了:
然后通过同步调用通过 http 获取 N 中的每个图像。我们在 NSOperation 中执行此组下载,因此我们不会阻塞 UI。
最后,在下载每个单独的图像之后,并且在完成该图像的 NSData 对象之后,我们调用:
我们的内存分配峰值行为下降到非常舒适的几兆字节,并停止增长。
We had a similar problem. While fetching lots of images over http, there was huge growth and a sawtooth pattern in the memory allocation. We'd see the system clean up, more or less, as it went, but slowly, and not predictably. Meanwhile the downloads were streaming in, piling up on whatever was holding onto the memory. Memory allocation would crest around 200M and then we'd die.
The problem was an NSURLCache issue. You stated that you tried [[NSURLCache sharedURLCache] removeAllCachedResponses]. We tried that, too, but then tried something a little different.
Our downloads are done in groups of N images/movies, where N was typically 50 to 500. It was important that we get all of N as an atomic operation.
Before we started our group of http downloads, we did this:
We then get each image in N over http with a synchronous call. We do this group download in an NSOperation, so we're not blocking the UI.
Finally, after each individual image download, and after we're done with our NSData object for that image, we call:
Our memory allocation peak behavior dropped to a very comfortable handful of megabytes, and stopped growing.
在这种情况下,您所看到的正是您应该看到的。
-[NSMutableDataappendData:]
增加其内部缓冲区的大小以保存新数据。由于NSMutableData
始终位于内存中,这会导致内存使用量相应增加。你在期待什么?如果这些图像的最终目标位于磁盘上,请尝试使用
NSOutputStream
而不是NSMutableData
。如果您随后想要显示图像,则可以在完成后创建一个指向该文件的UIImage
。In this case, you're seeing exactly what you're supposed to see.
-[NSMutableData appendData:]
increases the size of its internal buffer to hold the new data. Since anNSMutableData
is always located in memory, this causes a corresponding increase in memory usage. What were you expecting?If the ultimate destination for these images is on disk, try using an
NSOutputStream
instead ofNSMutableData
. If you then want to display the image, you can create aUIImage
pointing to the file when you're done.