简体   繁体   English

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

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

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 我已经制作了自定义UIImageView

DImageView.h 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 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. 一切正常,平滑滚动以及后台的asyncImage下载。

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. 问题是当我滚动UITableview应用程序内存不断增加,经过一段时间后,我得到了Receive memory waring 2/3时间然后应用程序崩溃。

When i use AsyncImageView class that time memory not increase and its working fine. 当我使用AsyncImageView类时,内存不会增加并且其工作正常。 But due to app requirement i saved all images to Document Directory and display from it if its available. 但由于应用程序的要求,我将所有图像保存到Document Directory并在其可用时显示。

i have tried with @autoreleasepool and release some variable but not getting success. 我已尝试使用@autoreleasepoolrelease一些变量但未获得成功。

I appreciated if any one have the solution to manage memory management. 我很感激,如果有人有解决方案来管理内存管理。

 **ARC is off in my application.** 

It's possible that UIImagePNGRepresentation returns non-autoreleased object - you can try to release it and see if that results in a crash. UIImagePNGRepresentation可能会返回非自动释放的对象 - 您可以尝试释放它并查看是否会导致崩溃。 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. 使用ObjectAlloc工具在Instruments中运行您的应用程序,应该立即明确哪些对象未被释放。 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. 您可以“跟踪”对象并在使用ObjectTracker解除分配时获取消息 - 但它是专为ARC设计的,因此您可能需要对其进行调整。 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 当表格视图使用单元格完成时,您可以接收一个委托方法,告诉您这样,然后您可以将单元格保留为nil(释放)和对象

  • 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 你使用downloadQueue是奇怪的 - 在你的实例中创建一次作为ivar,根据需要使用它,并在dealloc发布它

  • 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]; [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. 你可以在这里阅读更多关于它的信息,或者只是谷歌,你肯定会找到一大堆博客来阅读。

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 . 但是如果没有release一些对象和代码中的@autoreleasepool块,在滚动UITableView期间内存会不断增加。

From the Instrument i found that memory increase in UILableView and UIImageView . Instrument我发现UILableViewUIImageView中的内存增加。 I am using Custom UITableViewCell and in that file i havnt implement dealloc method. 我正在使用Custom UITableViewCell ,在该文件中我实现了dealloc方法。 So When i have implement dealloc method in UITableViewCell .m file and release & nil all object. 所以当我在UITableViewCell .m文件中实现dealloc方法并releasenil all对象时。

After that memory not increase during scroll TableView and its Resolved the issue. 在滚动TableView期间memory不增加并解决了问题。

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 根据我的理解,你的“getSavedImage”方法中存在一个问题,你必须手动管理内存而不是“自动释放”,所以我的建议是使用

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. '不要使用Autorelease,因为它一直停留在内存中,直到池没有耗尽'而且正因为这样你才会遇到内存问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 iOS:滚动UITableView时,应用程序崩溃/内存已满 - iOS : app crash / memory is filled in when scrolling a UITableView 当我滚动 UITableView 时图像消失 - Image Disappears when i am scrolling UITableView 如何在从Document Directory iOS读写图像期间处理内存警告 - How to handle memory warning during write and read image from Document Directory iOS 应用程序崩溃如果在滚动 UITableView 时 popViewController - App crash If popViewController while scrolling UITableView 快速滚动时带有AlamofireImage的Swift UItableview崩溃 - Swift UItableview with AlamofireImage crash when scrolling fast 从文档目录在webview中以html显示图像 - display image in html in webview from document directory 我可以将iOS应用崩溃报告保存到文档目录吗? - Can I save iOS App Crash Report to Document Directory? 当我触摸UITableView中的特定单元时,应用崩溃 - App crash when I touch specific cell in UITableView 从文档目录中删除所有文件后,UITableView无法重新加载 - UITableView is not reloading after I delete all files from Document Directory 如何解决 uiTableview 滚动应用程序崩溃,它在 swift 中引发数组索引超出范围异常? - How do I solve the uiTableview scrolling app crash which throws array index out of bound exception in swift?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM