NSURLConnection sendAsynchronousRequest永远不会释放内存

时间:2013-01-22 14:17:28

标签: ios nsurlconnection cfnetwork sendasynchronousrequest

我正在开发一个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 *pDataNSURLResponse *pResponse,因为它会在我崩溃时崩溃。

请建议......真的需要帮助来学习和欣赏目标......

Profiler

其他编辑:

感谢@robhayward,我将pImg和pDataPng作为__block变量放在外面,使用他的RHCacheImageView方式下载数据(NSData initWithContentOfURL)

还要感谢@JorisKluivers,第一个UIImage实际上可以重复显示,因为UIImageView同时识别jpg和png格式,只是我后来的处理需要png格式,我稍后会在需要时从磁盘读取

2 个答案:

答案 0 :(得分:2)

我首先要把它归结为你正在创建的图像和数据对象:

UIImage *pImg = [UIImage imageWithData:pData];
NSData *pDataPng = UIImagePNGRepresentation(pImg);

可能会挂起太长时间,也许会将它们放在块外,因为它们可能是在不同的线程上创建/发布的:

__block UIImage *pImg = nil;
__block NSData *pDataPng = nil;
[NSURLConnection sendAsynchronousRequest..

(如果可以,也考虑使用ARC)

我在Github上有一些代码可以在没有这个问题的情况下完成类似的工作,请随时查看:

https://github.com/robinhayward/RHCache/blob/master/RHCache/RHCache/Helpers/UIImageView/RHCacheImageView.m

答案 1 :(得分:2)

首先尝试简化代码。我做的事情:

  • 删除外部dispatch_async。这不是必需的,您的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];
}