Autorelease Not Being Called?

Discussion in 'Mac Programming' started by Darkroom, Jul 8, 2008.

  1. Darkroom Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #1
    i have a main window with 3 tool bar items that are set to autorelease... sending the app thru instruments, at exactly 30 seconds i receive this leak:
     

    Attached Files:

  2. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #2
    We can't help you unless we see your code. Don't forget that all autorelease does is automatically decrement your reference counts. It won't cause memory to be freed if it has been retained somewhere else.
     
  3. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #3
    sorry, i had originally thought i should have posted my code, but assumed that graphic would be enough... coffee is still kicking in ;)...

    this is ToolBar.m. code referring to memory is in blue:

    Code:
    #import "ToolBar.h"
    #import "ColorController.h"
    #import "FadeController.h"
    
    
    @implementation ToolBar
    
    - (void)awakeFromNib
    	{
    	ColorItem = [[NSToolbarItem alloc] initWithItemIdentifier:@"Color"];
    	[ColorItem setToolTip:@"Select Light Color"];
    	[ColorItem setPaletteLabel:[ColorItem label]];
    	[ColorItem setImage:[NSImage imageNamed:@"ColorLightToolBar.png"]];
    	[ColorItem setTarget:self];
    	[ColorItem setAction:@selector(selectColorTool:)];
    
    	WhiteItem = [[NSToolbarItem alloc] initWithItemIdentifier:@"White"];
    	[WhiteItem setToolTip:@"Reset To White Light"];
    	[WhiteItem setPaletteLabel:[WhiteItem label]];
    	[WhiteItem setImage:[NSImage imageNamed:@"WhiteLightToolBar.png"]];
    	[WhiteItem setTarget:self];
    	[WhiteItem setAction:@selector(selectWhiteTool:)];
    
    	FullScreenItem = [[NSToolbarItem alloc] initWithItemIdentifier:@"Full Screen"];
    	[FullScreenItem setToolTip:@"Full Screen Mode"];
    	[FullScreenItem setPaletteLabel:[FullScreenItem label]];
    	[FullScreenItem setImage:[NSImage imageNamed:@"FullScreenToolBar.png"]];
    	[FullScreenItem setTarget:self];
    	[FullScreenItem setAction:@selector(fullScreenModeTool:)];
    
    	[self setupToolbar];
    	}
    
    - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
    	itemForItemIdentifier:(NSString *)itemIdentifier
    	willBeInsertedIntoToolbar:(BOOL)flag
    	{
    	NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
    
    	if ([itemIdentifier isEqualToString:@"Color"])
    	{return ColorItem;}
    	if ([itemIdentifier isEqualToString:@"White"])
    	{return WhiteItem;}
    	if ([itemIdentifier isEqualToString:@"Full Screen"])
    	{return FullScreenItem;}
    	return [COLOR="blue"][item autorelease][/COLOR];
    	}
    
    - (NSArray *)toolbarAllowedItemIdentifiers:(NSToolbar*)toolbar
    	{
    	return [NSArray arrayWithObjects:NSToolbarSeparatorItemIdentifier, NSToolbarFlexibleSpaceItemIdentifier, @"Color", @"White", @"Full Screen", nil];
    	}
    
    - (NSArray *)toolbarDefaultItemIdentifiers:(NSToolbar*)toolbar
    	{
    	return [NSArray arrayWithObjects: NSToolbarFlexibleSpaceItemIdentifier, @"Color", @"White", @"Full Screen", NSToolbarFlexibleSpaceItemIdentifier, nil];
    	}
    
    - (void)setupToolbar
    	{
    	toolbar = [[NSToolbar alloc] initWithIdentifier:@"Toolbar"];
    	[toolbar setDelegate:self];
    	[toolbar setAutosavesConfiguration:YES];
    	[mainWindow2 setToolbar:[COLOR="Blue"][toolbar autorelease][/COLOR]];
    	}
    
    - (IBAction)selectColorTool:(id)sender
    	{
    	[myColorTool selectColor:nil];
    	}
    
    - (IBAction)selectWhiteTool:(id)sender
    	{
    	[myWhiteTool selectWhite:nil];
    	}
    
    
    - (IBAction)fullScreenModeTool:(id)sender
    	{
    	[myFullscreenTool fullScreenMode:nil];
    	}
    
    # pragma mark Dealloc
    
    [COLOR="blue"]- (void)dealloc
    	{
    	[toolbar autorelease];
    	[NSToolbarItem autorelease];
    	[super dealloc];
    	}[/COLOR]
    
    @end
    
     
  4. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #4
    The first thing that stands out is that those three toolbar items you create in awakeFromNib are never released. Presumably you would do that in dealloc.
     
  5. ElectricSheep macrumors 6502

    ElectricSheep

    Joined:
    Feb 18, 2004
    Location:
    Wilmington, DE
    #5
    This jumps at me right away:

    Code:
    - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
    	itemForItemIdentifier:(NSString *)itemIdentifier
    	willBeInsertedIntoToolbar:(BOOL)flag
    	{
    	NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
    
    	if ([itemIdentifier isEqualToString:@"Color"])
    	{return ColorItem; //if we get here, we exit this function without ever autoreleasing item }
    	if ([itemIdentifier isEqualToString:@"White"])
    	{return WhiteItem; //if we get here, we exit this function without ever autoreleasing item }
    	if ([itemIdentifier isEqualToString:@"Full Screen"])
    	{return FullScreenItem; //if we get here, we exit this function without ever autoreleasing item }
    	return [item autorelease];
    	}
    
    Basically, you have four exit points from your method, and only one of them actually puts item in the autorelease pool. This is the source of your memory leaks.
     
  6. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
  7. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #7
    memory isn't exactly my strong point... not yet anyway...

    i think i'm following what your saying correctly, but it still leaks... i've changed 2 methods:

    Code:
    - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
    	itemForItemIdentifier:(NSString *)itemIdentifier
    	willBeInsertedIntoToolbar:(BOOL)flag
    	{
    	NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
    
    	if ([itemIdentifier isEqualToString:@"Color"])
    	{return [ColorItem autorelease];}
    	if ([itemIdentifier isEqualToString:@"White"])
    	{return [WhiteItem autorelease];}
    	if ([itemIdentifier isEqualToString:@"Full Screen"])
    	{return [FullScreenItem autorelease];}
    	return [item autorelease];
    	}
    
    - (void)dealloc
    	{
    	[ColorItem autorelease];
    	[WhiteItem autorelease];
    	[FullScreenItem autorelease];
    	[toolbar release];
    	[NSToolbarItem release];
    	[super dealloc];
    	}
    
     
  8. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #8
    It still leaks because the "item" item that you create still only gets autoreleased if none of the other toolbar items are returned first.

    Move this line:
    Code:
    NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemI
    
    to the line immediately before
    Code:
    return item;
    
    Also, you don't want to autorelease the other 3 toolbar items in both this method and the dealloc method.
     
  9. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #9
    ok thanks a lot! that seems to have cleared up the memory leak for the toolbar by changing the methods to this:

    Code:
    - (NSToolbarItem *)toolbar:(NSToolbar *)toolbar
    	itemForItemIdentifier:(NSString *)itemIdentifier
    	willBeInsertedIntoToolbar:(BOOL)flag
    	{
    	if ([itemIdentifier isEqualToString:@"Color"])
    	{return ColorItem;}
    	if ([itemIdentifier isEqualToString:@"White"])
    	{return WhiteItem;}
    	if ([itemIdentifier isEqualToString:@"Full Screen"])
    	{return FullScreenItem;}
    	
    	NSToolbarItem *item = [[NSToolbarItem alloc] initWithItemIdentifier:itemIdentifier];
    	return item;
    	}
    
    - (void)dealloc
    	{
    	[super dealloc];
    	}
    
    
    but i still have a memory leak that i don't understand... what on earth are "general blocks"? this leak always seems to stay at 128 bytes, so it sounds pretty negligible to me - but maybe it will cause problems later down the road? besides, since i'm learning still i'd like to understand it... but maybe this super small memory leak is normal?
     

    Attached Files:

  10. Enuratique macrumors 6502

    Joined:
    Apr 28, 2008
    #10
    Nope, sorry, no leak is normal. It may be innocuous if they're small amounts of memory over a short-lived application, but you want to eradicate all of them. I'm taking a SWAG (scientific wild assed guess) that GeneralBlock means any memory allocated using C's alloc functions not being free()'d (malloc, calloc, realloc, etc). You doing that anywhere in your code?
     
  11. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #11
    there are lots of objects created by alloc in my code, but i'm pretty sure i've released them all... it would be more helpful if Instruments could be more specific (??)...

    i though it was normal because i opened Safari with Instruments and it is riddled with memory leaks much worse than my app (albeit more complex and probably more interesting)
     
  12. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #12
    Cocoa itself has minor leaks that show up in some situations, btw.
     
  13. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #13
    so these "general block" memory leaks at 128 bytes are normal?
     
  14. Catfish_Man macrumors 68030

    Catfish_Man

    Joined:
    Sep 13, 2001
    Location:
    Portland, OR
    #14
    Probably worth figuring out where it's coming from, although 128 bytes isn't much. Google MallocHistory and use it to get a backtrace on the allocation.
     
  15. Enuratique macrumors 6502

    Joined:
    Apr 28, 2008
    #15
    I think you can click the Extended Detail button on the bottom toolbar of the screen to see a stack trace of what line of code the leak was detected on. There's a good blog post on how to do just this here.

    HTH,

    Enuratique
     
  16. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #16
    awesome... that was really helpful... thanks for the link!

    [EDIT] each "general block" memory leak in instruments lead me to the same line of code in the same class (is it leaking 4 times?), which was a line that doesn't create an object by allocation: [mainWindow2 setContentView:normalscreenView];, so i don't think it's something i can really fix is it? here's the entire method:

    Code:
    - (void)awakeFromNib
    	{
    	if ([[prefsRadioGroup cellWithTag:1] state] == NSOnState)
    		{
    		SetSystemUIMode(kUIModeNormal, 0);
    		[mainWindow2 setAlphaValue:0.0];
    		[mainWindow2 makeKeyAndOrderFront:nil];
    		[COLOR="Blue"][mainWindow2 setContentView:normalscreenView];[/COLOR]
    		timer = [[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(normalScreenfadeIN2:) userInfo:nil repeats:YES] retain];
    		}
            }
    
    the only thing in that method that deals with memory management is the timer, but it's released in the "normalScreenfadeIN2:" method...

    while were on the subject of memory management, there is something that i find confusing. in Aaron Hillgass's book (Cocoa Programming, 3rd Ed.) he states: "when an object with a retain count of 1 is sent release, the dealloc method will be called. your dealloc method must release any objects that you were retaining and then the superclasses dealloc method." that makes sense to me, but i've seen quite a few class files that dealt with memory and didn't have any -(void)dealloc methods at all... including the "Leaky Demo Application" from Cocoa Is My Girlfriend... so are dealloc methods in any class that retains/releases objects optional?
     
  17. aLoC macrumors 6502a

    Joined:
    Nov 10, 2006
    #17
    When you call setContentView, if there was an existing content view already set then it will be released. If that released view has a member variable that it doesn't properly clean up in it's dealloc, that could be the cause.
     
  18. Duke Leto macrumors regular

    Joined:
    Mar 17, 2008
    #18
    So in every function you should place the returned value in an autorelease pool, and in every dealloc: method, you should release all of the properties to get rid of memory leaks? If that can cause it... I know where my leaks are coming from!
     
  19. Enuratique macrumors 6502

    Joined:
    Apr 28, 2008
    #19
    I'm going to have to agree with you on this one...
     
  20. Enuratique macrumors 6502

    Joined:
    Apr 28, 2008
    #20
    Yes, in general, the best practice is to call autorelease on any pointer/object you return from a function - at least that appears to be the convention set forth by Apple. That way the consumer of your method can retain the pointer/object if he needs it longer than it lives in the autorelease pool and still not have to worry about releasing it otherwise. Well, it depends on how the properties were declared (with the retain attribute specifically) but yes, in the dealloc method, you should definitely release anything you specifically called alloc with. If the property has the retain attribute, this means that the compiler will automatically call retain when you set the property - this way, if you set the property using a static initializer (typically these have autorelease called on them) they will have had retain called on them and will need to be manually released as if you had called alloc/init on it and the property did not have the retain attribute. Eg:

    Code:
    -.h file-
    NSData *someStuff;
    @property (nonatomic, retain) NSData *someStuff;
    
    -.m file-
    @synthesize someStuff;
    - (id)init {
      self.someStuff = [NSData data];
    }
    
    - (void)dealloc {
      [someStuff release]; /* NOT CALLING THIS WILL CAUSE A LEAK BECAUSE THE POINTER WAS RETAINED BY THE PROPERTY EVEN THOUGH WHAT WE GOT OUT OF THE NSDATA STATIC INITIALIZER WAS AUTORELEASED */
      [super dealloc];
    }
    
     
  21. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #21
    my content view is never released. it's always the same throughout the application.

    i'm still confused by the -(void)dealloc method. in every class where i had an object being released, i wrote a -(void)dealloc method in that class to release that classes objects. for example, in my prefs class, i have this:

    Code:
    - (IBAction)closePrefsWindow:(id)sender
    	{
    	timer = [[NSTimer scheduledTimerWithTimeInterval:0.02 target:self selector:@selector(preferenceWindowFadeOUT:) userInfo:nil repeats:YES] retain];
    	}
    
    - (void)preferenceWindowFadeOUT:(NSTimer *)theTimer
    	{
    	NSPanel* preferenceWindow;
    	if ([preferenceWindow alphaValue] > 0.0)
    		{
    		[preferenceWindow setAlphaValue:[preferenceWindow alphaValue] -0.1];
    		}
    		else
    		{
    		[timer invalidate];
    		[timer release];
    		[preferenceWindow close];
    		timer = nil;
    		}
    	}
    
    - (void)dealloc
    	{
    	[timer release];
    	[super dealloc];
    	}
    
    now, just out of curiousity i have removed all my dealloc methods from all of my classes and i still receive the same memory leaks, instead of receiving more as i had assumed since i was under the impression that if something is released it has to be also released thru the dealloc method of the same class, otherwise i won't be released... this is clearly not the case...
     
  22. Enuratique macrumors 6502

    Joined:
    Apr 28, 2008
    #22
    The dealloc is called when an object's retain count is 0. If your code happens to release objects before the containing class is released, then it's not necessary to release them in your dealloc method. It's just good practice to ensure that all objects have release called on them in dealloc. You may not be getting any more leaks because dealloc is most often called when the application exits and dealloc calls cascade from the highest level object in the object graph on down, any left over allocations get released when the autorelease pool created in the main method is released (typically the very last instruction in any program) and so Instruments won't find them. I would put those deallocs back.

    Pay attention to what aLoC was saying. Internally, when you set a property on an Apple-produced class, that setter was implemented like so:

    Code:
    - (void) setTheory: (Theory *)newTheory
    {
            Theory *oldTheory = nil;
    
            if ( theory != newTheory )        // If they're the same, do nothing
            {
                [self willChange];            // For Enterprise Objects only
                oldTheory = theory;        // Copy the reference
                theory = [newTheory retain];// First retain the new object
                [oldTheory release];        // Then release the old object
            }
    
            return;
    }
    
    
    I got this code from this blog post... I think it very succinctly talks about memory management in Cocoa and would serve anyone with memory management issues well to read and fully understand it. I read this a couple of times over before I fully understood what it was saying before writing my first Cocoa app and surprisingly I had no leaks when I was done developing and running it through Instruments for the first time.

    Your mainWindow2's contentView is having release called on it when you set it to a new one. So, double check the content view being replaced (especially if it's a custom content view) to make sure ITS member variables are all being released properly.

    Finally, look into the NSZombie stuff which will help also identify autorelease memory leaks.
     
  23. aLoC macrumors 6502a

    Joined:
    Nov 10, 2006
    #23
    Could you have set one up in Interface Builder, in which case there would be one set when your awakeFromNib is called?
     
  24. Darkroom thread starter Guest

    Darkroom

    Joined:
    Dec 15, 2006
    Location:
    Montréal, Canada
    #24
    yes, i have content view set up in Interface Builder... but isn't that what i'm calling in the awakeFromNib?

    *brain melts*
    *collapses*
    *totally dies*
     
  25. admanimal macrumors 68040

    Joined:
    Apr 22, 2005
    #25
    Just keep in mind that releasing a non-nil object more times than it has been retained will cause errors. This includes calling autorelease on the object multiple times.
     

Share This Page