Memory leak on reading images from document direct

2020-07-25 10:35发布

问题:

I am reading images from document directory in a loop.

for(iArrCount=0;iArrCount<[arrImages count];iArrCount++)
    {
UIButton *btnImage = [UIButton buttonWithType:UIButtonTypeCustom];
            btnImage.frame = CGRectMake(xRow, yRow, width, height);
            [btnImage addTarget:self action:@selector(imageDetails:) forControlEvents:UIControlEventTouchUpInside];
            btnImage.tag = iCount;
            if(iCount<[arrImages count])
            {


                NSString *workSpacePath=[[self applicationDocumentsDirectory] stringByAppendingPathComponent:[arrImages objectAtIndex:iCount]];
                [btnImage setBackgroundImage:[UIImage imageWithData:[NSData dataWithContentsOfFile:workSpacePath]] forState:UIControlStateNormal];
                [scrollImages addSubview:btnImage];
}
}  

On doing so, control goes to didRecieveMemoryWarning and application crashes. If I replace the image by a image from resource folder, application does not crashes. Why so?

回答1:

The problem is that your buttons are maintaining a reference to the full resolution image. What you will need to do is scale down the image to your button dimensions and set that scaled down image as your button background. Something like this should do the trick:

Create a category on UIImage... Let's call it UIImage+Scaler.h

// UIImage+Scaler.h
#import <UIKit/UIKit.h>

@interface UIImage (Scaler)
- (UIImage*)scaleToSize:(CGSize)size;
@end

And the implementation:

// UIImage+Scaler.m
#import "UIImage+Scaler.h"

#define kBitsPerComponent 8
#define kBitmapInfo       kCGImageAlphaPremultipliedLast

- (UIImage*)scaleToSize:(CGSize)size
{
    CGBitmapInfo bitmapInfo = kBitmapInfo;
    size_t bytesPerRow = size.width * 4.0;
    CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
    CGContextRef context = CGBitmapContextCreate(NULL, size.width, 
                                   size.height, kBitsPerComponent, 
                                   bytesPerRow, colorSpace, bitmapInfo);

    CGRect rect = CGRectMake(0.0f, 0.0f, size.width, size.height);
    CGContextDrawImage(context, rect, self.CGImage);

    CGImageRef scaledImageRef = CGBitmapContextCreateImage(context);
    UIImage* scaledImage = [UIImage imageWithCGImage:scaledImageRef];

    CGImageRelease(scaledImageRef);
    CGContextRelease(context);
    CGColorSpaceRelease(colorSpace);

    return scaledImage;
}

OK, now back to your code. Like the other posters said you'll need an autorelease pool.

CGSize buttonSize = CGSizeMake(width, height);

for(iArrCount=0;iArrCount<[arrImages count];iArrCount++)
{
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    UIButton *btnImage = [UIButton buttonWithType:UIButtonTypeCustom];
    btnImage.frame = CGRectMake(xRow, yRow, width, height);
    [btnImage addTarget:self 
                 action:@selector(imageDetails:)      
       forControlEvents:UIControlEventTouchUpInside];
    btnImage.tag = iCount;
    if(iCount<[arrImages count])
    {
        NSString *workSpacePath=[[self applicationDocumentsDirectory]  
                       stringByAppendingPathComponent:
                            [arrImages objectAtIndex:iCount]];
        UIImage* bigImage = [UIImage imageWithData:[NSData   
                             dataWithContentsOfFile:workSpacePath]];
        UIImage* smallImage = [bigImage scaleToSize:buttonSize];
        [btnImage setBackgroundImage:smallImage forState:UIControlStateNormal];
        [scrollImages addSubview:btnImage];
    }
    [pool drain]; // must drain pool inside loop to release bigImage
} 

Hope this solves for problem.



回答2:

Put your for loop in an autorelease pool:

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
for(...)
{
   ....
}
[pool release];


回答3:

Use NSAutoreleasePool. Keep your code inside an autoreleasepool.

for(iArrCount=0;iArrCount<[arrImages count];iArrCount++)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    ......
    ......
    [pool drain];
}


标签: iphone