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

Reply via email to