In response to an excellent first reply, below is a revision to my original post that corrects various points, and that also incorporates some changes to better focus my remaining questions - please disregard my original post in favor of the following:
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 understand. Below is 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), for the point of trying to compare what gets released/deallocated where and how.., I am specifically trying to understand how to override didReceiveMemoryWarning, setView and dealloc. Each point I am most uncertain about is labelled with "MUST CONFIRM". // // 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 UIView *primaryViewA; UIView *subViewA; UILabel *labelA; UIImageView *imageViewA; } @property (nonatomic, retain) NSString *myStringB; // Note: For the iPhone, unless encountering a compelling reason not to do so, generally make outlets properties, and retain them. //Note: Typically, an IBOutlet specifies an instance variable that references some object that is defined in your NIB file. However, just as an academic exercise, in this example, primaryViewA will be created programmatically, even though declared as an outlet. // Preferred syntax is to use the IBOutlet tag on the @property line, rather than in the interface declaration above @property (nonatomic, retain) IBOutlet 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 primaryViewA; @synthesize subViewA; @synthesize labelA; @synthesize imageViewA; // Implement loadView if you want to create a view hierarchy programmatically - (void)loadView { NSString* string00Local = [[NSString alloc] initWithString:@"00"]; //Note: String constants, like @"abc", are specially generated by the compiler as static objects; release and retain have no effect on them. So there will be no need to release string00Local myStringA = [[NSString alloc] initWithString:@"A"];// Note that myStringA is an instance variable but not a property, so can't call self.myStringA. Will need to be released in dealloc self.myStringB = [NSString stringWithFormat:@"B"]; // instance variable - must be released in dealloc. And note that not using "self", alternatively setting 'myStringB = ...' would be wrong - it would bypass your @synthesized accessors. In every method except for -init and -dealloc, accessing your properties via self.propertyName. myStringC = [NSString stringWithFormat:@"C"];// myStringC is an instance variable but not a property (so can't call self.myStringC).Will need to be released in dealloc NSMutableString * stringCompleteMutable = [NSMutableString stringWithString:string00Local]; // local variable set with a constructor that handles release, so there will be no need to release it [stringCompleteMutable appendString: @", "]; [stringCompleteMutable appendString: myStringA]; [stringCompleteMutable appendString: @", "]; [stringCompleteMutable appendString: self.myStringB]; [stringCompleteMutable appendString: @", "]; [stringCompleteMutable appendString: myStringC]; // The property primaryViewA already has a retain count of 1, and if you were to set it using [UIView alloc] init...] you would increase its retain count to 2. Instead, allocate it to a temporary variable, assign that to the @property and then release your temporary variable. UIView *viewTempA = [[UIView alloc]initWithFrame:[[UIScreen mainScreen] applicationFrame]]; self.primaryViewA = viewTempA; [viewTempA release]; self.primaryViewA.backgroundColor = [UIColor redColor]; // See previous comment - same reasoning applies UIView *viewTempB = [[UIView alloc]initWithFrame:[[UIScreen mainScreen] applicationFrame]]; self.subViewA = viewTempB; [viewTempB release]; self.subViewA.backgroundColor = [UIColor greenColor]; // See previous comment - same reasoning applies (use a temporary variable here as well). UILabel *labelTemp = [[UILabel alloc]init]; self.labelA = labelTemp;// instance variable - will be released in dealloc [labelTemp release]; CGRect rectA = CGRectMake(0,0, 320,50);// CGRect is a scalar structure that's local to the scope it's defined in. It has no concept of retain/release/autorelease, the same as NSInteger, NSUInteger, BOOL and CGFloat self.labelA = [[UILabel alloc] initWithFrame:rectA]; self.labelA.font = [UIFont systemFontOfSize:12.0]; self.labelA.textAlignment = UITextAlignmentCenter; self.labelA.text = stringCompleteMutable; self.labelA.textColor = [UIColor whiteColor]; self.labelA.backgroundColor = [UIColor blueColor]; [self.labelA setText:stringCompleteMutable]; [self.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); self.imageViewA = [[UIImageView alloc]initWithFrame:imageRect]; // instance variable - will be released in dealloc self.imageViewA.backgroundColor = [UIColor clearColor]; self.imageViewA.image = theImage; [self.subViewA addSubview:imageViewA]; [self.primaryViewA addSubview:subViewA]; self.view = primaryViewA; } // Method setView: // Overrides setter for UIViewController property view. - (void)setView:(UIView *)theView; { if (theView == nil){ // release views and label 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 them in method didReceiveMemoryWarning because... 1. MUST CONFIRM: we have declared them 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 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; // Even though no setters were defined for this object, still set it to nil after releasing it for precisely the same reason that you set properties to nil. [myStringA release], myStringA = nil; [myStringC release], myStringC = nil; // Releases the view if it doesn't have a superview [super didReceiveMemoryWarning]; } - (void)dealloc { // 3. MUST CONFIRM: No longer sure about this case... // Original reasoning: 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. // Versus... // While UIViewController uses self.view = nil (or [self setView:nil]) in its' dealloc, this is not the recommended way to release your retained objects in your -dealloc method. Since a property access is still just a method call it may have unwanted side-effects that you may not even be aware of, think subclasses, you should therefore call release directly on any retained objects you may have, regardless of their status as properties or not. self.myStringB = 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: [primaryViewA release], primaryViewA = nil; // rather than: self.primaryViewA = nil; ... And note that this does need to be explicitly released; the ViewController's view was set to it, but it must still be released separately // 4. MUST CONFIRM: Correctly releasing the next three objects? They are properties, but not outlets... [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; // Note don't need to explicitly release the ViewController's view - the superclass will do this. [super dealloc]; } @end _______________________________________________ Cocoa-dev mailing list (Cocoa-dev@lists.apple.com) 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: http://lists.apple.com/mailman/options/cocoa-dev/archive%40mail-archive.com This email sent to [EMAIL PROTECTED]