Reduce Memory Usage in iOS App without leaks Reduce Memory Usage in iOS App without leaks ios ios

Reduce Memory Usage in iOS App without leaks


You say you are using a table view. Although cells are reused automatically, this makes it very easy to make mistakes and create too many objects.1 common error is allocating objects (eg. UIImageView) in the cellForRowAtIndexPath method, as this means every time a cell is reused a new UIImageView is added to it as well as keeping the old ones. So double check what is going on in your cellForRowAtIndexPath method.


I decided to add full code for memory saving, if you are using GIF files, modify UIImage scale method (Found it here, an Stackoverflow). As said GangstaGraham in SD Image exist method sd_animatedImageByScalingAndCroppingToSize

@interface UIImage (Scaling)-(UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize;-(UIImage*) croppedImageWithRect: (CGRect) rect;@end@implementation UIImage (Scaling)- (UIImage *)imageByScalingProportionallyToSize:(CGSize)targetSize {    if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {        if ([[UIScreen mainScreen] scale] == 2.0) {            targetSize.height *= 2.0f;            targetSize.width *= 2.0f;        }    }    NSUInteger width = targetSize.width;    NSUInteger height = targetSize.height;    UIImage *newImage = [self resizedImageWithMinimumSize: CGSizeMake (width, height)];    return [newImage croppedImageWithRect: CGRectMake ((newImage.size.width - width) / 2, (newImage.size.height - height) / 2, width, height)];}-(CGImageRef)CGImageWithCorrectOrientation{    if (self.imageOrientation == UIImageOrientationDown) {        //retaining because caller expects to own the reference        CGImageRetain([self CGImage]);        return [self CGImage];    }    UIGraphicsBeginImageContext(self.size);    CGContextRef context = UIGraphicsGetCurrentContext();    if (self.imageOrientation == UIImageOrientationRight) {        CGContextRotateCTM (context, 90 * M_PI/180);    } else if (self.imageOrientation == UIImageOrientationLeft) {        CGContextRotateCTM (context, -90 * M_PI/180);    } else if (self.imageOrientation == UIImageOrientationUp) {        CGContextRotateCTM (context, 180 * M_PI/180);    }    [self drawAtPoint:CGPointMake(0, 0)];    CGImageRef cgImage = CGBitmapContextCreateImage(context);    UIGraphicsEndImageContext();    return cgImage;}-(UIImage*)resizedImageWithMinimumSize:(CGSize)size{    CGImageRef imgRef = [self CGImageWithCorrectOrientation];    CGFloat original_width  = CGImageGetWidth(imgRef);    CGFloat original_height = CGImageGetHeight(imgRef);    CGFloat width_ratio = size.width / original_width;    CGFloat height_ratio = size.height / original_height;    CGFloat scale_ratio = width_ratio > height_ratio ? width_ratio : height_ratio;    CGImageRelease(imgRef);    return [self drawImageInBounds: CGRectMake(0, 0, round(original_width * scale_ratio), round(original_height * scale_ratio))];}-(UIImage*)drawImageInBounds:(CGRect)bounds{    UIGraphicsBeginImageContext(bounds.size);    [self drawInRect: bounds];    UIImage *resizedImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    return resizedImage;}-(UIImage*)croppedImageWithRect:(CGRect)rect{    UIGraphicsBeginImageContext(rect.size);    CGContextRef context = UIGraphicsGetCurrentContext();    CGRect drawRect = CGRectMake(-rect.origin.x, -rect.origin.y, self.size.width, self.size.height);    CGContextClipToRect(context, CGRectMake(0, 0, rect.size.width, rect.size.height));    [self drawInRect:drawRect];    UIImage* subImage = UIGraphicsGetImageFromCurrentImageContext();    UIGraphicsEndImageContext();    return subImage;}-(UIImage *) resizableImageWithCapInsets2: (UIEdgeInsets) inset{    if ([self respondsToSelector:@selector(resizableImageWithCapInsets:resizingMode:)])    {        return [self resizableImageWithCapInsets:inset resizingMode:UIImageResizingModeStretch];    }    else    {        float left = (self.size.width-2)/2;//The middle points rarely vary anyway        float top = (self.size.height-2)/2;        return [self stretchableImageWithLeftCapWidth:left topCapHeight:top];    }}@end

And UIImageView:

#import <SDWebImage/SDImageCache.h>@implementation UIImageView (Scaling)-(void)setImageWithURL:(NSURL*)url scaleToSize:(BOOL)scale{    if(url.absoluteString.length < 10) return;    if(!scale){        [self setImageWithURL:url];        return;    }    __block UIImageView* selfimg = self;    __block NSString* prevKey = SPRINTF(@"%@_%ix%i", url.absoluteString, (int)self.frame.size.width, (int)self.frame.size.height);    __block UIImage* prevImage = nil;    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);    dispatch_async(queue, ^ {        prevImage = [[SDImageCache sharedImageCache] imageFromDiskCacheForKey:prevKey];        if(prevImage){            dispatch_async(dispatch_get_main_queue(), ^ {                [self setImage:prevImage];            });        }else{            [[SDWebImageDownloader sharedDownloader] downloadImageWithURL:url options:SDWebImageDownloaderFILOQueueMode progress:nil completed:^(UIImage *image, NSData *data, NSError *error, BOOL finished) {                if(error){                    [selfimg setImageWithURL:url scaleToSize:scale];                }else{                    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);                    dispatch_async(queue, ^ {                        prevImage = [image imageByScalingProportionallyToSize:self.frame.size];                        if(finished)                            [[SDImageCache sharedImageCache] storeImage:prevImage forKey:prevKey];                        dispatch_async(dispatch_get_main_queue(), ^ {                            [self setImage:prevImage];                        });                    });                }            }];        }    });    return;}-(void)setImageWithURL:(NSURL *)url placeholderImage:(UIImage *)placeholder scaleToSize:(BOOL)scale{    [self setImage:placeholder];    [self setImageWithURL:url scaleToSize:scale];}@end


I would suggest, that you use Instruments and Heapshot Analysis. bbum wrote an article about it at his blog.

Here is a quick overview:

  1. Start your App in Instruments and select the Allocations template
  2. Wait some time after your App start to settle down
  3. In Allocations, press Mark Heap; This is your baseline.
  4. Use your app and return to the same screen as in #2. Press Mark Heap again.
  5. Repeat that for some time.

If you see a steady growth of memory, you can drill down in the heapshots and see all objects allocated. That should give you a good start to reduce your memory footprint.