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 12:43:54 -0600
On Nov 29, 2008, at 9:45 AM, Glenn Bloom wrote:
Ashley,
Can't thank you enough. I just posted the following, hoping to pre-
empt other folks from wasting time correcting me on various points
you addressed - and I hope, refocusing things on my major
questions. I have of course been through the memory management
documentation you referred me to, and much else on the subject, but
clearly need to work through it all alot more. Will do so feverishly
beginning now, but again, thought it best to fix what I could
quickly in a revised post, to avoid wasting lots of folks time...
Also I didn't see your response to me posted in the mailing list -
so wasn't sure if/how I should give credit for your help?
Hmmm... I'm pretty sure that I cc'ed the list on that first email. I
believe that if the list sees that a message already went to you it
won't send it to you again though so that may explain why you didn't
see it there. Oh well, no bother.
Unfortunately I still saw a couple of things that I don't think I made
myself clear on earlier.
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
If your assignment statement were simply: NSString *string00Local =
@"00"; then yes you wouldn't have to release it. What you're doing
though is creating a new object based on the static string @"00". Any
object created through an -init... type method is owned by you and
would need to be released.
A @"something" constant is an object on its' own standing. You do not
have to use it as a constant in creating an object. You'd probably
only use it in an initWithString: message if you were creating a
mutable string from it. I'd probably prefer [@"something" mutableCopy]
though since it's more readable IMO.
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.
This isn't wrong from a memory management standpoint since the object
returned from stringWithFormat: is then retained by the property. But
it looks odd. I'd have simply set self.myStringB = @"B" directly like
the others.
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
This would also need to be retained since you want the value to stay
around. You're probably not seeing crashes on any of these because of
how NSString conservatively creates objects. I'm relatively certain
that if you were to create two objects, objectA = @"C" and objectB =
[NSString stringWithFormat:@"C"] and compared their pointer value that
they'd be equivalent. Really though that is an implementation detail
and you should be simply assigning myStringC = @C";
I'm not sure if you intended to but you're creating two UILabel
objects here.
// 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];
The first one was created in that stanza.
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];
Then you created another object here.
// 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.
You are correct about this second confirmation. Since your 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.
I'd also suggest that you don't really need to keep your own copy of
the view's instance variable since it's readily accessible from
self.view.
- (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;
Pedantically this is correct, although if you were truly directly
storing only string constants here (ie. myStringC = @"something";)
then you wouldn't per se need to release them. It is good form though,
especially if these strings end up being later created through some
other class where their origin is unknown.
// 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;
Yes, this is correct. Conceptually, generic synthesized accessors look
something like this:
- (void)setLabelA:(id)newValue {
if (labelA != newValue) {
[labelA release];
labelA = [newValue retain];
}
}
Using self.labelA = nil calls this method with newValue set to nil.
This then evaluates equivalently to [labelA release], labelA = nil;
It's not recommended to use them during -dealloc and -init though
because they may not be the generic synthesized accessors and they may
have side-effects that are unwanted during init and dealloc. Whether
something is a property or an outlet though is no distinction here.
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