UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
- Subject: UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
- From: "Glenn Bloom" <email@hidden>
- Date: Sat, 29 Nov 2008 00:44:44 -0500
In the course of trying to understand UIViewController memory warnings on
the iPhone, I've found various useful threads online, and in particular, was
very glad to follow the numerous recent posts in this forum with the subject
'Outlets / IBOutlet declarations'.
In response, I've written a test app to confirm what I think I have gathered
so far. I will be very appreciative if anyone can take a look at the
interface and implementation below and respond to any of the five (5)
different comments tagged with "MUST CONFIRM". I have also heavily
commented most everything else related to memory management (gone
overboard), thinking it relevant to this example, at least for folks like
myself, newer to the subject.
Essentially, what is below are an interface and implementation that modify
the iPhone View-Based Application template to display a UILabel and a
UIImage within a couple of nested views. I define and work with different
instance variables and (local variables as well), solely for the point of
trying to compare what gets released/deallocated where and how...
Thanks to anyone in advance for any assistance.
//
// Test00ViewController.h
// Test00
//
#import <UIKit/UIKit.h>
@interface Test00ViewController : UIViewController {
NSString *myStringA; // Will NOT be a property
NSString *myStringB;
NSString *myStringC; // Will NOT be a property
NSString *myStringD;
IBOutlet UIView *primaryViewA;
IBOutlet UIView *subViewA;
IBOutlet UILabel *labelA;
IBOutlet UIImageView *imageViewA;
}
@property (nonatomic, retain) NSString *myStringB;
@property (nonatomic, retain) NSString *myStringD;
// Note: For the iPhone, unless encountering a compelling reason not to do
so, generally make outlets properties, and retain them.
@property (nonatomic, retain) UIView *primaryViewA;
@property (nonatomic, retain) UIView *subViewA;
@property (nonatomic, retain) UILabel *labelA;
@property (nonatomic, retain) UIImageView *imageViewA;
@end
//
// Test00ViewController.m
// Test00
//
#import "Test00ViewController.h"
@implementation Test00ViewController
@synthesize myStringB;
@synthesize myStringD;
@synthesize primaryViewA;
@synthesize subViewA;
@synthesize labelA;
@synthesize imageViewA;
// Implement loadView if you want to create a view hierarchy
programmatically
- (void)loadView {
NSString* string00 = [[NSString alloc] init];// local variable that will
need to be released locally
string00 = @"00";
[string00 release];
NSString* string01 = [NSString string];// local variable set with a
constructor that handles release, so there will be no need to release it
string01 = @"01";
myStringA = [[NSString alloc] init];// instance variable that will need to
be released in dealloc
myStringA = @"A";
myStringB = [NSString stringWithFormat:@"B"];// instance variable - must be
released in dealloc, even though set with a constructor that handles release
myStringC = [NSString stringWithFormat:@"C"];// instance variable - must be
released in dealloc, even though set with a constructor that handles release
myStringD = [NSString stringWithFormat:@"D"];// instance variable - must be
released in dealloc, even though set with a constructor that handles release
NSMutableString * stringEMutable = [NSMutableString
stringWithString:string00]; // local variable set with a constructor that
handles release, so there will be no need to release it
[stringEMutable appendString: @", "];
[stringEMutable appendString: string01];
[stringEMutable appendString: @", "];
[stringEMutable appendString: myStringA];
[stringEMutable appendString: @", "];
[stringEMutable appendString: myStringB];
[stringEMutable appendString: @", "];
[stringEMutable appendString: myStringC];
[stringEMutable appendString: @", "];
[stringEMutable appendString: myStringD];
printf("contents of stringEMutable: %s\n", [stringEMutable UTF8String]);
primaryViewA = [[UIView alloc]initWithFrame:[[UIScreen mainScreen]
applicationFrame]]; // instance variable - will be released in dealloc
primaryViewA.backgroundColor = [UIColor redColor];
subViewA = [[UIView alloc]initWithFrame:[[UIScreen mainScreen]
applicationFrame]]; // instance variable - will be released in dealloc
subViewA.backgroundColor = [UIColor greenColor];
labelA = [[UILabel alloc]init];// instance variable - will be released in
dealloc
CGRect rectA = CGRectMake(0,0, 320,50);// local variable set with a
constructor that handles release, so there will be no need to release it
labelA = [[UILabel alloc] initWithFrame:rectA];
labelA.font = [UIFont systemFontOfSize:12.0];
labelA.textAlignment = UITextAlignmentCenter;
labelA.text = stringEMutable;
labelA.textColor = [UIColor whiteColor];
labelA.backgroundColor = [UIColor blueColor];
[labelA setText:stringEMutable];
[subViewA addSubview:labelA];
CGRect imageRect;
UIImage *theImage;
theImage = [UIImage imageNamed:@"Button00A.png"]; // local variable set
with a constructor that handles release, so there will be no need to release
it
int w = theImage.size.width;
int h = theImage.size.height;
imageRect = CGRectMake(50.0, 50.0, w,h);// local variable set with a
constructor that handles release, so there will be no need to release it
imageViewA = [[UIImageView alloc]initWithFrame:imageRect]; // instance
variable - will be released in dealloc
imageViewA.backgroundColor = [UIColor clearColor];
imageViewA.image = theImage;
[subViewA addSubview:imageViewA];
[primaryViewA addSubview:subViewA];
self.view = primaryViewA;
}
/*
Implement viewDidLoad if you need to do additional setup after loading the
view.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
-
(BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
// Method setView:
// Overrides setter for UIViewController property view.
- (void)setView:(UIView *)theView;
{
if (theView == nil){
// release outlets when the argument is nil
// As long as this UIViewController subclass retains its top level view
then all of the view's subviews will also be retained. However, they should
all be released when the UIViewController releases its view... And we can't
release the outlets in method didReceiveMemoryWarning because... 1. MUST
CONFIRM: we have declared our outlets as properties, with "retain", and we
can't determine accurately within didReceiveMemoryWarning when the view
controller's view is in fact released (except by calling setView), so we
can't conditionally release them within the didReceiveMemoryWarning method
(except by actually setting the controller's view).
self.labelA = nil;
self.imageViewA = nil;
self.subViewA = nil;
self.primaryViewA = nil; // 2. MUST CONFIRM: We also release this outlet
here, not in didReceiveMemoryWarning, despite the fact that the controller's
view is set to this rather than it be added to the controller's view as a
subview.
}
[super setView:theView];
}// End Method setView:
- (void)didReceiveMemoryWarning {
// Release anything that's not essential, such as cached data (meaning
instance variables, and what else...?)
// Obviously can't access local variables such as defined in method
loadView, so can't release them here
// We can set some instance variables as nil, rather than call the release
method on them, if we have defined setters that retain nil and release their
old values (such as through use of @synthesize). This can be a better
approach than using the release method, because this prevents a variable
from pointing to random remnant data. Note in contrast, that setting a
variable directly (using "=" and not using the setter), would result in a
memory leak.
self.myStringB = nil;
self.myStringD = nil;
[myStringA release];// No setter defined - must release it this way
[myStringC release];// No setter defined - must release it this way
/* 3. MUST CONFIRM: NOT necessary to release outlets here - See override of
setView instead.
self.labelA = nil;
self.imageViewA = nil;
self.subViewA = nil;
*/
// Releases the view if it doesn't have a superview
[super didReceiveMemoryWarning];
}
- (void)dealloc {
// We can set some instance variables as nil, rather than call the release
method on them, if we have defined setters that retain nil and release their
old values (such as through use of @synthesize). This can be a better
approach than using the release method, because this prevents a variable
from pointing to random remnant data. Note in contrast, that setting a
variable directly (using "=" and not using the setter), would result in a
memory leak.
self.myStringB = nil;
self.myStringD = nil;
[myStringA release];// No setter defined - must release it this way
[myStringC release];// No setter defined - must release it this way
// A caveat to the choice illustrated above (setting an instance variable as
nil versus using the release method)... Because UIViewController currently
implements its dealloc method using the setView: accessor method (rather
than simply releasing the variable directly...), self.anOutlet = nil will be
called in dealloc as well as in response to a memory warning... This will
lead to a crash in dealloc. The remedy is to ensure that outlet variables
are also set to nil in dealloc as follows:
// [anOutlet release], anOutlet = nil;
[labelA release], labelA = nil; // rather than: self.labelA = nil;
[imageViewA release], imageViewA = nil; // rather than: self.imageViewA =
nil;
[subViewA release], subViewA = nil; // rather than: self.subViewA = nil;
[primaryViewA release], primaryViewA = nil; // rather than:
self.primaryViewA = nil; ... 4. MUST CONFIRM: Not sure if this need to be
explicitly released or if it will be released when the ViewController's view
is automatically released (because the ViewController's view was set to it)?
// 5. MUST CONFIRM: Don't need to explicitly release the ViewController's
view.
[super dealloc];
}
@end
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden