0

我正在开发一个 iOS 应用程序,它向我的串行队列分派了相当多的任务。任务是从我的网络服务器下载图像,将其保存到磁盘,然后显示在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]的东西,因为分析器显示该函数是占用所有内存的函数......

但是,我也不太确定 dispatch_*** 和阻塞的东西,我之前一直使用 C 和 C++ 代码和 pthread,但是在阅读了 Apple 的关于从线程迁移的文档之后,我决定尝试 GCD ,objective-c 太麻烦了,我不知道如何释放它NSData *pDataNSURLResponse *pResponse因为每当我这样做时它都会崩溃。

请指教...真的需要帮助来学习和欣赏objective-c ...

探查器

附加编辑:

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

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

4

2 回答 2

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

于 2013-01-22T14:50:43.373 回答
2

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

  • 移除外部 dispatch_async。这不是必需的,您sendAsynchronousRequest已经是异步的。这也消除__block了队列中另一个变量的需要。
  • pImg您从收到的中创建一个命名的图像pData,然后将其转换回NSDatapng 类型,然后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];
}
于 2013-01-22T15:32:31.970 回答