Seemingly simple operation -- releasing what the nib loaded
Hi there, I have a nib with one my of my view controllers in it, which has quite a few full screen background images in UIImageViews. I don't have outlets to those, because they are static. I find that I'm unable to unload those imageviews, even if I release the view that contains them, I'm not recovering the memory. Is there some way to do this?
Apparently images assigned in IB are loaded into image views using imageNamed. imageNamed caches the images in a way that makes them unloadable. You could load the images in viewDidLoad with initWithContentsOfFile and then assign them to the views.
Ahh... thanks. Apple are geniuses, giving us memory warnings and not providing a way to deal with them using the "approved" "official" interface builder techniques.
I'll do what you suggest. More pointless work and extra iboutlets...
So I've made a lot of changes and I'm now loading most of the significantly large images from code instead of IB. This works great on the simulator, I can clearly see in ObjectAlloc that I'm getting my memory back. But on the device, I'm not. There's something really weird about that. Does the device not actually give up memory? This is really frustrating because it's a hefty app and eventually I get a crash and there doesn't seem to be any way for me to release the memory.
What do you mean? The memory I'm trying to unload is being created programmatically, there is no associated IBOutlet.
Clarification... here is my source:
Code:
// UIViewWithBackgroundImage.h
#import <UIKit/UIKit.h>
// This is specifically for use in a NIB, to avoid losing the memory for the background image.
// Do NOT set the background image in IB. Set it programmatically!
// That way memory management will actually work and you'll actually be able to recover
// the memory from the bg image when you release this view.
@interface UIViewWithBackgroundImage : UIView {
UIImageView * _backgroundImageView;
UIImage * _backgroundImage;
}
@property (retain) UIImageView * _backgroundImageView;
- (void)setBackgroundNamed:(NSString *)imageName;
- (IBAction)releaseImage;
@end
Code:
// UIViewWithBackgroundImage.m
#import "UIViewWithBackgroundImage.h"
@implementation UIViewWithBackgroundImage
@synthesize _backgroundImageView;
-(void)awakeFromNib; {
[super awakeFromNib];
NSLog(@" vvv awakeFromNib in UIViewWithBackgroundImage with self.frame = %@", NSStringFromCGRect(self.frame));
_backgroundImageView = [[UIImageView alloc] initWithFrame:self.frame];
[self addSubview:_backgroundImageView];
[self sendSubviewToBack:_backgroundImageView];
}
- (void)dealloc {
//NSLog(@" vvv dealloc in UIViewWithBackgroundImage");
[super dealloc];
}
// @param imageName must be an image in the root of the bundle resource directory
- (void)setBackgroundNamed:(NSString *)imageName; {
NSString * path = [NSString stringWithFormat:@"%@/%@", [[NSBundle mainBundle] resourcePath], imageName];
_backgroundImage = [UIImage imageWithContentsOfFile:path];
_backgroundImageView.image = _backgroundImage; // this should retain it automatically
NSLog(@" vvv UIViewWithBackgroundImage: setImageNamed has retain count = %d", [_backgroundImage retainCount]);
}
- (IBAction)releaseImage; {
NSLog(@" vvv releaseImage in UIViewWithBackgroundImage");
[_backgroundImageView removeFromSuperview];
[_backgroundImageView release];
_backgroundImageView = nil;
}
@end
On the simulator I get my memory back on releaseImage, but on the device I don't, according to ObjectAlloc in Instruments.
Last edited by sbwoodside; 03-27-2009 at 01:13 AM.
Reason: clarification
If you're using ObjectAlloc then it can tell you exactly what kind of blocks of memory are not being dealloced.
I don't see in your dealloc method that you release the imageView. Also, my suggestion regarding the way that imageview's in nibs load their images referred to the image, not the imageview. What I mean is that it's ok to have an imageview in the nib, just don't have an image assigned to it. You seem to be building both the image and the imageview in awakeFromNib. I think you only need to build the image. In this way nothing needs to be retained by your vc.
If you're using ObjectAlloc then it can tell you exactly what kind of blocks of memory are not being dealloced.
I tried to figure this out but I couldn't find that memory in the list. Can you point me to some document that explains how to track a specific allocation?
Quote:
Originally Posted by PhoneyDeveloper
I don't see in your dealloc method that you release the imageView.
Yes... I'm calling releaseImage before release always. (for debugging purposes). It would be easy to add [self releaseImage] in the release method.
Quote:
Originally Posted by PhoneyDeveloper
Also, my suggestion regarding the way that imageview's in nibs load their images referred to the image, not the imageview. What I mean is that it's ok to have an imageview in the nib, just don't have an image assigned to it. You seem to be building both the image and the imageview in awakeFromNib. I think you only need to build the image. In this way nothing needs to be retained by your vc.
I have many views that are using this, and so instead of inserting a UIImageView manually in IB in each one I'm doing it here programmatically.
I tried to figure this out but I couldn't find that memory in the list. Can you point me to some document that explains how to track a specific allocation?
I don't have a good pointer to the docs. In ObjectAlloc it shows you a list of allocated objects. The main view is a kind of summary view. For instance I see CFString, CFNumber, CFDictionary, a bunch of different General Blocks and so on in the list, along with their stats of how much memory they take up. If I move my mouse over CFString I see a little arrow appear next to it. If I click that little arrow I get a new list of all the CFString objects in memory. If I select each one of them I can see the stack trace of where it was allocated.
I'm not saying this isn't tedious, it is. But the info is somewhere in there of what kind of blocks of memory are being allocated and not released.
I'm not saying this isn't tedious, it is. But the info is somewhere in there of what kind of blocks of memory are being allocated and not released.
I messed around with that quite a bit and couldn't find anything in the area of CFImage that was anywhere near large enough to be my images. Also, I find that the automatic leak detection doesn't seem to work very well.
However, at a high level on the simulator I could easily see in the ObjectAlloc graph the numbers going up and down, whereas running on device it was not going down except just a little bit.