Re: UIViewController memory warnings | didReceiveMemoryWarning | setView | releasing outlets | releasing properties
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 20:20:15 -0600
On Nov 29, 2008, at 2:58 PM, Glenn Bloom wrote:
// Implement loadView if you want to create a view hierarchy
programmatically
- (void)loadView {
/* snip */
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];
This is fine and there's nothing wrong with it but it might be more
readable to create your string with a format string:
NSString *stringComplete = [NSString stringWithFormat:@"%@, %@, %@",
string00Local, myStringA, self.myStringB];
That's really a stylistic point though, it's not like this code will
be running in a loop generating hundreds of strings where you'd want
to send as few messages as possible.
self.labelA.text = stringCompleteMutable;
[self.labelA setText:stringCompleteMutable];
These two lines are equivalent but I think it's just remnants of some
copy and pasting though.
As I understand it, all instances of UIViewController will receive the
didReceiveMemoryWarning message when the OS warns the app. At that
point you should clear out any caches. Since they all receive this
message, even if they're visible, you can't know at that point whether
the view will be released or not. Currently the only way to know that
your view is going away is when you receive a setView:nil message.
It's at that point, when your view is being released, that you should
also release and nil out any references you may have to objects that
are part of that view.
// 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; // 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. Since the view is going away here you need to remove all
references to it, and in fact, must remove this reference
specifically as it won't be dealloc'ed until you do since you've
retained it.
}
[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.
// Note that because myStringA was only set to point to a static
string, it doesn't really need to be released here, but pedantically
this is correct - this is good form, anticipating for instance, a
modification by which this string ends up being later created
through some other class in some other manner
[myStringA release], myStringA = nil;
// Releases the view if it doesn't have a superview
[super didReceiveMemoryWarning];
}
- (void)dealloc {
// In contrast to how you deallocated in didReceiveMemoryWarning,
in dealloc you don't want to employ setters. As a general rule, you
don't want to employ setters in either init methods or in dealloc.
Conceptually, generic synthesized accessors look something like this:
// - (void)setLabelA:(id)newValue {
// if (labelA != newValue) {
// [labelA release];
// labelA = [newValue retain];
// }
// }
// Using self.labelA = nil would call this method with newValue set
to nil. This would evaluate equivalently to [labelA release],
labelA = nil;
// However, It's not recommended to use such a setter during -
dealloc and -init because they may not be the generic synthesized
accessors and they may have side-effects that are unwanted during
init and dealloc.... So just use release:
[myStringA release];
[myStringB release]; // Note that this is different than what is
employed in didReceiveMemoryWarning, where the following was
preferrable, i.e., the setter was used:
// self.myStringB = nil;
// primaryViewA does need to be explicitly released; the
ViewController's view was set to it, but it must still be released
separately
// Again you don't want to use a setter (self.primaryViewA = nil)
in dealloc, for the reasons stated previously.
// And because UIViewController currently implements its dealloc
method using the setView: accessor method (rather than simply
releasing the variable directly...), you also want to set it to nil
in dealloc as follows, in addition to releasing it.
[primaryViewA release], primaryViewA = nil;
// likewise...
[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: you don't need to explicitly release the ViewController's
view - the superclass will do this.
[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