我正在开发一个iOS应用程序,它将大量任务分配给我的串行队列。任务是从我的Web服务器下载图像,将其保存到磁盘,然后显示在UIImageView
上。但是,[NSURLConnection sendAsynchrousRequest]
将继续占用越来越多的内存,直到iOS终止我的进程。
下载程序方法如下所示:
// dispatch_queue_t is created once by: m_pRequestQueue = dispatch_queue_create( "mynamespace.app", DISPATCH_QUEUE_SERIAL);
- (void) downloadImageInBackgroundWithURL:(NSString*) szUrl {
__block typeof(self) bSelf = self;
__block typeof(m_pUrlRequestQueue) bpUrlRequestQueue = m_pRequestQueue;
dispatch_async( m_pRequestQueue, ^{
NSAutoreleasePool *pAutoreleasePool = [[NSAutoreleasePool alloc] init];
NSURLRequest *pRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:szUrl]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:URL_REQUEST_TIMEOUT];
[NSURLConnection sendAsynchronousRequest:pRequest queue:bpUrlRequestQueue completionHandler:^(NSURLResponse *pResponse, NSData *pData, NSError *pError) {
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
if ( pError != nil ) {
} else {
// convert image to png format
UIImage *pImg = [UIImage imageWithData:pData];
NSData *pDataPng = UIImagePNGRepresentation(pImg);
bool bSaved = [[NSFileManager defaultManager] createFileAtPath:szCacheFile contents:pDataPng attributes:nil];
}
__block typeof(pDataPng) bpDataPng = pDataPng;
__block typeof(pError) bpError = pError;
dispatch_sync( dispatch_get_main_queue(), ^ {
NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
UIImage *pImage = [[UIImage alloc] initWithData:bpDataPng];
// display the image
[pImage release];
// NSLog( @"image retain count: %d", [pImage retainCount] ); // 0, bad access
[autoreleasepool drain];
});
}
[pPool drain];
}]; // end sendAsynchronousRequest
[pAutoreleasePool drain];
}); // end dispatch_async
} // end downloadImageInBackgroundWithURL
我很确定这是[NSURLConnection sendAsynchronousRequest]
内部的内容,因为分析器显示该功能是占用所有内存的功能......
但是,我也不太确定发送_ ***和阻止事情,我以前总是使用C和C ++代码和pthread,但是在阅读了Apple关于从线程迁移的文档后,我决定给出尝试GCD,objective-c非常麻烦,我不确定如何释放NSData *pData
和NSURLResponse *pResponse
,因为它会在我崩溃时崩溃。
请建议......真的需要帮助来学习和欣赏目标......
其他编辑:
感谢@robhayward,我将pImg和pDataPng作为__block变量放在外面,使用他的RHCacheImageView方式下载数据(NSData initWithContentOfURL)
还要感谢@JorisKluivers,第一个UIImage实际上可以重复显示,因为UIImageView同时识别jpg和png格式,只是我后来的处理需要png格式,我稍后会在需要时从磁盘读取
答案 0 :(得分:2)
我首先要把它归结为你正在创建的图像和数据对象:
UIImage *pImg = [UIImage imageWithData:pData];
NSData *pDataPng = UIImagePNGRepresentation(pImg);
可能会挂起太长时间,也许会将它们放在块外,因为它们可能是在不同的线程上创建/发布的:
__block UIImage *pImg = nil;
__block NSData *pDataPng = nil;
[NSURLConnection sendAsynchronousRequest..
(如果可以,也考虑使用ARC)
我在Github上有一些代码可以在没有这个问题的情况下完成类似的工作,请随时查看:
答案 1 :(得分:2)
首先尝试简化代码。我做的事情:
sendAsynchronousRequest
已经异步。这也消除了队列中另一个__block
变量的需要。 pImg
创建一个名为pData
的图片,然后将其转换回png类型的NSData
,稍后再次创建另一个图片pImage
。而不是反复转换,只需重复使用第一个图像。您甚至可以将原始pData
写入磁盘(除非您确实需要磁盘上的png格式)。我自己没有编译下面的代码,所以它可能包含一些错误。但它是一个更简单的版本,可能有助于解决泄漏。
- (void) downloadImageInBackgroundWithURL:(NSString*)szUrl
{
__block typeof(self) bSelf = self;
NSAutoreleasePool *pAutoreleasePool = [[NSAutoreleasePool alloc] init];
NSURLRequest *pRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:szUrl]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:URL_REQUEST_TIMEOUT];
[NSURLConnection sendAsynchronousRequest:pRequest queue:m_pRequestQueue completionHandler:^(NSURLResponse *pResponse, NSData *pData, NSError *pError) {
NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
if (pError) {
// TODO: handle error
return;
}
// convert image to png format
__block UIImage *pImg = [UIImage imageWithData:pData];
// possibly just write pData to disk
NSData *pDataPng = UIImagePNGRepresentation(pImg);
bool bSaved = [[NSFileManager defaultManager] createFileAtPath:szCacheFile contents:pDataPng attributes:nil];
dispatch_sync( dispatch_get_main_queue(), ^ {
// display the image in var pImg
});
}];
[pAutoreleasePool drain];
}