Memory continuously increase then app crash when i display image from document directory during scrolling UITableview

泄露秘密 提交于 2020-01-11 09:33:09

问题


My Requirement is download all images in application memory and display it from local if its available.

Below is my code to access image from local and if its not available then it will download then display.

[cell.imgProfilePic processImageDataWithURLString:cData.PICTURE];

I have made custom UIImageView class

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

Everything is working fine with smooth scrolling as well as asyncImage download in background.

The issue is when i scroll UITableview application memory is continuously increase and after some time i got Receive memory waring 2/3 time then application crash.

When i use AsyncImageView class that time memory not increase and its working fine. But due to app requirement i saved all images to Document Directory and display from it if its available.

i have tried with @autoreleasepool and release some variable but not getting success.

I appreciated if any one have the solution to manage memory management.

 **ARC is off in my application.** 

回答1:


It's possible that UIImagePNGRepresentation returns non-autoreleased object - you can try to release it and see if that results in a crash. Obviously you are not releasing something, but nothing other than the image representation appears obvious.

A few other comments:

  • run your app in Instruments, using the ObjectAlloc tool, and it should be immediately obvious what objects are not dealloced. If you don't know Instruments, well, its time now to learn it.

  • you can 'track' objects and get a message when they are dealloced using ObjectTracker - however it was designed for ARC so you may need to tweak it. If you use it you would see a message when each of your objects are dealloced

  • when the table view is done with a cell, there is a delegate method that you can receive that tells you so, and you can then nil (release) and objects the cell retains

  • your use of downloadQueue is bizarre - create it once in your instance as an ivar, use it as you need, and in dealloc release it

  • you hide the activity spinner on the main queue, but don't start it on the main queue

  • you command the activity view to remove itself from its superview, but then look for in in the subviews and try to remove it there:

[self.activityView removeFromSuperview];

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

In the end, Instruments is what you want. You can read up more about it here, or just google and you will surely find a slew of blogs to read.




回答2:


Yes Finally i have resolved it.

The code which is in Question is working fine now. but Without release some objects and @autoreleasepool block which is in code, memory was increase continuously during scroll UITableView.

From the Instrument i found that memory increase in UILableView and UIImageView. I am using Custom UITableViewCell and in that file i havnt implement dealloc method. So When i have implement dealloc method in UITableViewCell .m file and release & nil all object.

After that memory not increase during scroll TableView and its Resolved the issue.




回答3:


As per my Understanding there is an issue in your "getSavedImage" Method you have to manage memory Manually instead of 'autorelease' so as My suggestion is use

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

instead of this.

[[UIImage imageWithContentsOfFile:filePath] retain];

'Don't Use Autorelease because it has staying in memory until pool not drain' and just because of this you got an memory issue.



来源:https://stackoverflow.com/questions/29017579/memory-continuously-increase-then-app-crash-when-i-display-image-from-document-d

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!