• Open Menu Close Menu
  • Apple
  • Shopping Bag
  • Apple
  • Mac
  • iPad
  • iPhone
  • Watch
  • TV
  • Music
  • Support
  • Search apple.com
  • Shopping Bag

Lists

Open Menu Close Menu
  • Terms and Conditions
  • Lists hosted on this site
  • Email the Postmaster
  • Tips for posting to public mailing lists
Re: UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties


  • Subject: Re: UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
  • From: Ashley Clark <email@hidden>
  • Date: Sat, 29 Nov 2008 01:38:28 -0600

On Nov 28, 2008, at 11:44 PM, Glenn Bloom wrote:

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.

I'll take a stab at it.


//
//  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

I think the suggested syntax here is to move the IBOutlet tag to the @property line and out of the @interface section as well, for example


@property (nonatomic, retain) IBOutlet UIView *someView;


//
//  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 {

You're leaking strings here.

NSString* string00 = [[NSString alloc] init];// local variable that will
need to be released locally

At this point, string00 is an empty string that you'll need to release.

string00 = @"00";

After this line, string00 is now pointing to a new string object and you've leaked the previous empty string object.


[string00 release];

String constants, like @"00", are specially generated by the compiler as static objects; release and retain have no effect on them.


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";

Same thing applies for string01 and myStringA here. Also it might be important to point out that setting your myStringA properties in this way bypasses your @synthesized accessors. In every method except for - init and -dealloc you should be accessing your properties via self.propertyName.


So your myStringA assignment should look like this:

self.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

In this case, since you're not using your accessor methods, you are not retaining these objects and when the autorelease pool is drained you'll be left with dangling references. Use your accessors.


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]);

If you use NSLog to print to the console you can print objects with the %@ format tag. This has the effect of calling -description on the object and printing that string. For NSString objects -description returns the string value itself.


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];

This is a tricky part here. You should be setting it via your synthesized accessors but since [UIView alloc] init...] returns an object that you already own, setting it via self.primaryViewA will retain it again, meaning that when your dealloc releases it you'll still have one more object retaining it unless you release it here. The same applies for subViewA.


Ideally you should allocate it to a temporary variable, assign that to your @property and then release your temporary variable.

UIView *view = [[UIView alloc] init...];
self.primaryViewA = view;
[view release];

I'm a little confused why you're defining IBOutlets though and then creating them programmatically. Typically an IBOutlet specifies an instance variable that references some object that is defined in your NIB file.

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

Technically a CGRect is a scalar structure that's local to the scope it's defined in. It has no concept of retain/release/autorelease, in the same way that NSInteger, NSUInteger, BOOL and CGFloat are.



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];

More of the same as before with labelA here.

CGRect imageRect;
UIImage *theImage;
theImage = [UIImage imageNamed:@"Button00A.png"]; // local variable setwith 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;


}

/* snip */

// 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:

Someone else will have to comment on these two specifically. I can say that since you have retained these objects though you need to release them if you are no longer interested in them here. So, since you retained primaryViewA in one instance variable and then assigned it to your superclass' view property, you really have two references to it. The setView:nil method that you're overriding here by default only releases one of those references.


- (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

Even if you haven't defined setters for these objects I'd still recommend setting them to nil when you release them for precisely the same reason that you set the properties to nil. It can even be done on one line.


[myStringA release], myStringA = nil;


/* 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

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.


// 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)?

Since you have a retained reference to it, you must release it.

// 5. MUST CONFIRM: Don't need to explicitly release the ViewController's
view.

Your superclass retained the view when you set it (or loaded it from a NIB file); the superclass is responsible for it, not you.


[super dealloc];
}

@end


I definitely recommend reading over the Memory Management guidelines again since you've made quite a few mistakes with what you have and haven't retained.

http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

Ashley

_______________________________________________

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


References: 
 >UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties (From: "Glenn Bloom" <email@hidden>)

  • Prev by Date: MVC question
  • Next by Date: Re: analysing image to find blobs
  • Previous by thread: UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
  • Next by thread: Re: UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
  • Index(es):
    • Date
    • Thread