下载并在后台存储大量图像时出现内存问题

时间:2013-11-04 16:44:52

标签: ios image stored-procedures memory-management download

我不喜欢经常问一个问题,但这次没找到问题的解决方案所以我不得不问你如何解决我的问题。

我创建了一个对象 OffLineModeBrain ,用于在应用程序正常工作时下载大量背景图像,我制作代码,一切正常我只是内存有问题,我已经尝试在每个解决方案中按时间释放它但没有结果。 基本上在我的 OffLineModeBrain 中,我有一个方法来执行此操作并加载一个渐进条,这是代码:

-(void)downloadAndSaveByArray:(NSArray *)array{


    //PATH
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *paths = NSSearchPathForDirectoriesInDomains( NSCachesDirectory,
                                                         NSUserDomainMask, YES);
    NSString *cachesDirectory = [paths objectAtIndex:0];
    NSString *cachesImageFolderPath = [cachesDirectory stringByAppendingPathComponent:@"com.hackemist.SDWebImageCache.default"];

    dispatch_queue_t queueImages = dispatch_queue_create("Images", NULL);
    dispatch_queue_t queueDownload = dispatch_queue_create("Download", NULL);
    //dispatch_queue_t queueStore = dispatch_queue_create("Store", NULL);

    int total = array.count;
    __block int progess = 0;
    __weak OffLineModeBrain *wself = self;
    dispatch_sync(queueImages, ^{
    for ( NSString *image in array) {
        if (_stop) {
            //
            NSLog(@"stop");
            _stop = NO;
            _downloading = NO;
            break;

        }else{
            //CHECK IMAGE IN LOCAL
            BOOL success = [fileManager fileExistsAtPath:
                            [cachesImageFolderPath stringByAppendingPathComponent:
                             [NSString stringWithFormat:@"/%@", [[image MD5String] lowercaseString]]]];
            if (!success) {
                dispatch_sync(queueDownload, ^{
                    if (!_stop) {

                        //DOWNLOAD AND SAVE
                        NSError *error;
                        NSURL  *url =  [NSURL URLWithString:image];
                        _urlData = [NSData dataWithContentsOfURL:url] ;
                        NSLog(@"Download");

                        if (_urlData)
                        {
                            //dispatch_sync(queueStore, ^{

                                NSString  *filePath = [NSString stringWithFormat:@"%@/%@", cachesImageFolderPath, [image MD5String]];
                                [_urlData writeToFile:filePath atomically:NO];
                                NSLog(@"Stored");

                                //RETURN PROGRESS
                                progess = progess + 1;
                                [wself.delegate downloadImagesProgress:progess ofTotal:total];

                            //});


                        }else{
                            NSLog(@"Download Images erroe: %@", error);

                            //RETURN PROGRESS
                            progess = progess + 1;
                            [wself.delegate downloadImagesProgress:progess ofTotal:total];
                        }
                        //dsf
                    }else{
                        NSLog(@"stop");
                        progess = progess + 1;
                        if (progess == total) {
                            _stop = NO;

                        }
                        _downloading = NO;
                    }
                });


            }else{
                NSLog(@"Local");

                //RETURN PROGRESS
                progess = progess + 1;
                [wself.delegate downloadImagesProgress:progess ofTotal:total];
            }
        }

    }//FOR IMAGES ENDED
    });

}

这是downloadImagesProgress: ofTotal:每次更新加载栏的方法。问题开始时,我开始逐个下载图像,然后他们没有释放,仍然在堆中直到结束循环,如果在结束之前停止循环它们也是释放,问题是当循环运行它们在下载和存储后,我没有发布并仍在堆中,我在仪器中看到的是 CFData(存储)每次都会成长,并且会在循环结束时释放。

1 个答案:

答案 0 :(得分:0)

我猜,声明

_urlData = [NSData dataWithContentsOfURL:url] ;

将返回“自动释放”对象。

这会产生以下后果:

由于您没有在内部循环中使用自动释放池,因此您可以有效地“保留”自动释放池中的所有返回数据对象,该池在外部内部循环中耗尽。

为了缓解这个问题,简单地在循环中放置一个自动释放池,以便释放dataWithContentsOfURL:中返回的对象,同时保留内循环的自动释放池的范围:

for (...) {
    @autoreleasepool {
        ...
        NSData* data = [NSData dataWithContentsOfURL:url];
        ...
    }
}

虽然使用自动释放池可能有助于解决此问题,但老实说,您还应该检查并重新实现当前的方法。例如,使用dispatch_sync()的方式没有意义。

如果您使用NSURLConnection的异步方便方法 - 或者甚至更好地使用委托方法,您还可以大大改进您的代码。 (我这样说,尽管我知道异步启动异步任务列表并不容易,除非你对异步模式有很多经验。)