当我在滚动UITableview期间显示文档目录中的图像时,内存不断增加然后应用程序崩溃

时间:2015-03-12 18:28:47

标签: ios iphone uitableview memory-management uiimage

我的要求是下载应用程序内存中的所有图像,如果可用,则从本地显示。

以下是我从本地访问图片的代码,如果没有,则会下载然后显示。

[cell.imgProfilePic processImageDataWithURLString:cData.PICTURE];

我已经制作了自定义UIImageView

DImageView.h

    #import <UIKit/UIKit.h>
    @interface DImageView : UIImageView
    @property (nonatomic, strong) UIActivityIndicatorView *activityView;
    - (void)processImageDataWithURLString:(NSString *)urlString;
    + (UIImage *)getSavedImage :(NSString *)fileName;
    @end

DImageView.m

#import "DImageView.h"
#define IMAGES_FOLDER_NAME  @"DImages"

@implementation DImageView

- (id)initWithFrame:(CGRect)frame
{
    self = [super initWithFrame:frame];
    if (self)
    {    }
    return self;
}
- (void)dealloc
{
    self.activityView = nil;
    [super dealloc];
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
    self = [super initWithCoder:aDecoder];
    if (self)
    {
        [self initWithFrame:[self frame]];
    }
    return self;
}
- (void)processImageDataWithURLString:(NSString *)urlString
{
    @autoreleasepool
    {
    UIImage * saveImg =[DImageView getSavedImage:urlString];
    if (saveImg)
    {
        @autoreleasepool
        {
        dispatch_queue_t callerQueue = dispatch_get_main_queue();
        dispatch_async(callerQueue, ^{

            @autoreleasepool{
            [self setImage:saveImg];
            }
        });
    }
    }
    else
    {
        [self showActivityIndicator];
        NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];

        dispatch_queue_t callerQueue = dispatch_get_main_queue();
        dispatch_queue_t downloadQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);
        __block NSError* error = nil;
        dispatch_async(downloadQueue, ^{

            @autoreleasepool
            {
                NSData * imageData = [NSData dataWithContentsOfURL:url options:NSDataReadingUncached error:&error];
                if (!error)
                {
                    dispatch_async(callerQueue, ^{
                        @autoreleasepool {
                        UIImage *image = [UIImage imageWithData:imageData];
                        [self setImage:image];
                        [self hideActivityIndicator];
                        [self saveImageWithFolderName:IMAGES_FOLDER_NAME AndFileName:urlString AndImage:imageData];
                        }
                    });
                }
            }
        });
        dispatch_release(downloadQueue);
    }
    }
}
- (void) showActivityIndicator
{
    self.activityView = [[UIActivityIndicatorView alloc]initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)];
    self.activityView.autoresizingMask = UIViewAutoresizingFlexibleLeftMargin | UIViewAutoresizingFlexibleTopMargin | UIViewAutoresizingFlexibleRightMargin | UIViewAutoresizingFlexibleBottomMargin;
    self.activityView.hidesWhenStopped = TRUE;
    self.activityView.backgroundColor = [UIColor clearColor];
    self.activityView.activityIndicatorViewStyle = UIActivityIndicatorViewStyleGray;

    [self addSubview:self.activityView];
    [self.activityView startAnimating];
}
- (void) hideActivityIndicator
{
    CAAnimation *animation = [NSClassFromString(@"CATransition") animation];
    [animation setValue:@"kCATransitionFade" forKey:@"type"];
    animation.duration = 0.4;;
    [self.layer addAnimation:animation forKey:nil];
    [self.activityView stopAnimating];
    [self.activityView removeFromSuperview];

    for (UIView * view in self.subviews)
    {
        if([view isKindOfClass:[UIActivityIndicatorView class]])
            [view removeFromSuperview];
    }
}
- (void)saveImageWithFolderName:(NSString *)folderName AndFileName:(NSString *)fileName AndImage:(NSData *) imageData
{
    @autoreleasepool{
    NSFileManager *fileManger = [[NSFileManager defaultManager] autorelease];
    NSString *directoryPath =  [[NSString stringWithFormat:@"%@/%@",[DImageView  applicationDocumentsDirectory],folderName] autorelease];

    if (![fileManger fileExistsAtPath:directoryPath])
    {
        NSError *error = nil;
        [fileManger createDirectoryAtPath:directoryPath withIntermediateDirectories:YES attributes:nil error:&error];
    }
    fileName = [DImageView fileNameValidate:fileName];
    NSString *filePath = [[NSString stringWithFormat:@"%@/%@",directoryPath,fileName] autorelease];

    BOOL isSaved = [imageData writeToFile:filePath atomically:YES];
    if (!isSaved)DLog(@" ** Img Not Saved");
    }
}

+ (NSString *)applicationDocumentsDirectory
{
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
    return basePath;
}

+ (UIImage *)getSavedImage :(NSString *)fileName
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    fileName = [DImageView fileNameValidate:fileName];

    NSFileManager * fileManger = [[NSFileManager defaultManager] autorelease];
    NSString * directoryPath =  [[NSString stringWithFormat:@"%@/%@",[DImageView applicationDocumentsDirectory],IMAGES_FOLDER_NAME] autorelease];
    NSString * filePath = [[NSString stringWithFormat:@"%@/%@",directoryPath,fileName] autorelease];

    if ([fileManger fileExistsAtPath:directoryPath])
    {
        UIImage *image = [[[UIImage imageWithContentsOfFile:filePath] retain]autorelease];
        if (image)
            return image;
        else
            return nil;
    }
    [pool release];
    return nil;
}

+ (NSString*) fileNameValidate : (NSString*) name
{
    name = [name stringByReplacingOccurrencesOfString:@"://" withString:@"##"];
    name = [name stringByReplacingOccurrencesOfString:@"/" withString:@"#"];
    name = [name stringByReplacingOccurrencesOfString:@"%20" withString:@""];
    return name;
}
@end

一切正常,平滑滚动以及后台的asyncImage下载。

问题是当我滚动UITableview应用程序内存不断增加时,经过一段时间我得到Receive memory waring 2/3时间,然后应用程序崩溃。

当我使用AsyncImageView类时,内存不会增加并且工作正常。但是由于应用程序的要求,我将所有图像保存到Document Directory,并在可用时显示它。

我尝试了@autoreleasepoolrelease一些变量,但没有取得成功。

如果有人有管理内存管理的解决方案,我很感激。

 **ARC is off in my application.** 

3 个答案:

答案 0 :(得分:3)

UIImagePNGRepresentation可能会返回非自动释放的对象 - 您可以尝试释放它并查看是否会导致崩溃。显然你没有发布任何东西,但除了图像表示之外别无其他。

其他一些评论:

  • 使用ObjectAlloc工具在Instruments中运行您的应用程序,应该立即明白哪些对象未被释放。如果您不了解乐器,那么现在是时候学习它了。

  • 您可以“跟踪”对象并在使用ObjectTracker取消分配时收到消息 - 但它是专为ARC设计的,因此您可能需要对其进行调整。如果您使用它,您将在每个对象被解除分配时看到一条消息

  • 当使用单元格完成表格视图时,您可以接收一个告诉您的委托方法,然后您可以将单元格保留为nil(释放)和对象

  • 您对downloadQueue的使用很奇怪 - 在您的实例中创建一次作为ivar,根据需要使用它,并在dealloc发布它

  • 隐藏主队列上的活动微调器,但不要在主队列上启动它

  • 您命令活动视图将其自身从超级视图中删除,但随后在子视图中查找并尝试将其删除:

[self.activityView removeFromSuperview];

for (UIView * view in self.subviews)
{
    if([view isKindOfClass:[UIActivityIndicatorView class]])
        [view removeFromSuperview];
}

最后,仪器就是你想要的。你可以在这里阅读更多关于它的信息,或者只是谷歌,你肯定会找到一大堆博客来阅读。

答案 1 :(得分:2)

是的,最后我已经解决了。

问题中的代码现在正常工作。但是如果代码中没有release一些对象和@autoreleasepool块,则在滚动UITableView期间内存会不断增加。

Instrument我发现UILableViewUIImageView内存增加了。我正在使用自定义UITableViewCell,并且在该文件中我实现了dealloc方法。因此,当我在dealloc UITableViewCell文件和.m&amp;中实施release方法时nil所有对象。

在滚动memory期间TableView未增加并且其解决了问题。

答案 2 :(得分:0)

根据我的理解,你的&#34; getSavedImage&#34;您必须手动管理记忆的方法,而不是&#39; autorelease&#39; ,以便我的建议可以使用

UIImage *image = [[UIImage alloc] initWithContentsOfFile:filePath]
and also release it after use of it. means after '[self setImage:saveImg];'
[saveImg release]

而不是这个。

[[UIImage imageWithContentsOfFile:filePath] retain];

&#39;不要使用 Autorelease ,因为它一直停留在内存中,直到游泳池不流失&#39;正因为如此,你才遇到了记忆问题。