views:

59

answers:

2

I have an IntroViewController with a very large image, which increases the memory of my app by about 1.5MB. The image is set on a UIImageView within the View Controller's NIB.

Once the intro has finished, I call release on the IntroViewController, which then successfully calls dealloc on itself and calls release on the large UIImageView. However, my memory in Instruments doesn't seem to come back down again. I can test the memory difference by simply renaming the image to the UIImageView in the NIB so it can't find anything, and then the memory drops by about 1.5MB.

So why am I not getting that memory back? Is there something that I'm missing which is stopping the UIImageView from being properly dealloc'd?

Thanks,

:-Joe

------- ADDED: CODE ------

#import <UIKit/UIKit.h>

@class AppDelegate_iPhone;

@interface IntroViewController : UIViewController
{
    AppDelegate_iPhone *appDelegate;

    UIImageView *wheelImageView;
    UIView *copyOverlayView;
}

@property (nonatomic, retain) IBOutlet UIImageView *wheelImageView;
@property (nonatomic, retain) IBOutlet UIView *copyOverlayView;

- (void)startAnimation;

@end

@implementation IntroViewController

@synthesize wheelImageView, copyOverlayView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    appDelegate = (AppDelegate_iPhone *)[[UIApplication sharedApplication] delegate];
    [self startAnimation];
}

- (void)viewDidDisappear:(BOOL)animated
{
    [self.wheelImageView removeFromSuperview];
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
}

- (void)viewDidUnload
{
    [super viewDidUnload];
    self.wheelImageView.image = nil;
    appDelegate = nil;
}

- (void)dealloc
{
     [wheelImageView release];
     [copyOverlayView release];

    [super dealloc];
}

- (void)startAnimation
{
    self.wheelImageView.transform = CGAffineTransformMakeScale(0.5, 0.5);
    [UIView beginAnimations:@"wheelScale" context:nil];
    [UIView setAnimationDuration:2.0];
    [UIView setAnimationDelegate:self];
    [UIView setAnimationDidStopSelector:@selector(wheelScaleDidFinish)];
    self.wheelImageView.transform = CGAffineTransformMakeScale(1.0, 1.0);
    [UIView commitAnimations];

    [UIView beginAnimations:@"copyOverlayFade" context:nil];
    [UIView setAnimationDuration:1.0];
    [UIView setAnimationDelay:1.0];
    self.copyOverlayView.alpha = 0;
    [UIView commitAnimations];
}

- (void)wheelScaleDidFinish
{
    [appDelegate introAnimationFinished];
}

@end

---- EDIT

Can anyone please help? I'm stumped, and it's causing my app to crash on the iPhone due to high memory :( can't figure out how to get rid of the stupid image! Grrrr...

+1  A: 

The UIImageView is being released, but are you sure you don't have a phantom retain on it somewhere else? If I were you I would try to track down its retains and releases. You can either do that with funky debugger tricks, tools like malloc_history, or simply by make a subclass of UIImageView:

@interface DebugImageView @end
@implementation DebugImageView
  - (id) retain {
    NSLog(@"retained");
    return [super retain];
  }

  - (void) release {
    NSLog(@"release");
    [super release];
  }

  - (void) dealloc {
    NSLog(@"dealloc");
    [super dealloc];
  }

Just change the class of the UIImageView object in IB to DebugImageView, and you should be good to go. You can also put break points on those to see where they are called, or just make calls to backtrace() where I have logging statements.

If the object is in fact being deleted, then your memory increase is probably not serious, and is most likely due to either a large reusable buffer object that is lazily evicted, or heap fragmentation. 1.5MB does seem a bit high for that sort of thing though, so I suspect it is a an over retain.

Louis Gerbarg
Thanks - I tried that, and I see a LOT of traces! It does seem to be calling "dealloc" at the end though. Would it be possible that since it is part of a UIView animation, that it's holding onto the image? Do I need to release or un-delegate the UIView animation sequence?
Joe
PS in Leaks, I can see that it's still hanging around - I have a Malloc of 1.39MB with the responsible library ImageIO...
Joe
It is conceivable the image could be copied into a buffer backing a CALayer that you are retaining indirectly by accidentally over retaining some part of the animation, but without a lot more details that would be pretty difficult to figure out. Also, what init method are you using, is there any chance you are holding onto the buffer used to create the image (like an NSData)?
Louis Gerbarg
I'm not using CALayer directly, but I am creating UIView animations with ID names... Is it possible that's holding onto a reference? The weird thing is that I see the "dealloc" trace. Anyway I've posted all the code to the ViewController above :-) thanks!
Joe
A: 

Okay so I found the solution, but am very surprised by it!

Not only did I have to take the UIImageView out of IB and create it dynamically, I had to avoid the use of the convenience method for loading the image. Instead of:

wheelImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:@"wheel-intro.png"];
[self.view addSubview:self.wheelImageView];
[wheelImageView release];

I had to use:

NSString *imagePath = [[NSBundle mainBundle] pathForResource:@"wheel-intro" ofType:@"png"];
wheelImage = [[UIImage alloc] initWithContentsOfFile:imagePath];
wheelImageView = [[UIImageView alloc] initWithImage:wheelImage];
[wheelImage release];
[self.view addSubview:self.wheelImageView];
[wheelImageView release];

Now when I do [wheelImageView removeFromSuperview] in the viewDidDisappear handler, everything gets cleaned up correctly.

What I find REALLY bizarre, is that putting the image in Interface Builder is identical in memory management to using a convenience method in XCode. And neither work properly! Note to self: Stop using convenience methods...

Joe
The reason is that UIImage imageNamed: and friends maintain an image cache. Under pressure the system will release the memory.
Louis Gerbarg
So although there is autorelease, some objects have their own cache? That's a little known fact, thanks. However, you say that under pressure the system will release the memory, but in practise for me, the phone crashes from lack of memory, and Leaks still shows the image cached!
Joe