Become a MacRumors Supporter for $50/year with no ads, ability to filter front page stories, and private forums.

Darkroom

Guest
Original poster
Dec 15, 2006
2,445
0
Montréal, Canada
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:
 

Attachments

  • Picture 1.png
    Picture 1.png
    23 KB · Views: 92
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.
 
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.

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
 
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.
 
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.
 
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];
	}
 
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.
 
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?
 

Attachments

  • Picture 3.png
    Picture 3.png
    63.7 KB · Views: 74
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?

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

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

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?
 
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.
 
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!
 
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.

I'm going to have to agree with you on this one...
 
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!

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

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

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.
 
my content view is never released. it's always the same throughout the application.

Could you have set one up in Interface Builder, in which case there would be one set when your awakeFromNib is called?
 
Could you have set one up in Interface Builder, in which case there would be one set when your awakeFromNib is called?

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

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.
 
Register on MacRumors! This sidebar will go away, and you'll see fewer ads.